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 =  (function() {
65             try {  
66                 document.createEvent("TouchEvent");  
67                 return true;  
68             } catch (e) {  
69                 return false;  
70             } 
71             
72         })();
73     // remove css image flicker
74         if(isIE && !isIE7){
75         try{
76             document.execCommand("BackgroundImageCache", false, true);
77         }catch(e){}
78     }
79     
80     Roo.apply(Roo, {
81         /**
82          * True if the browser is in strict mode
83          * @type Boolean
84          */
85         isStrict : isStrict,
86         /**
87          * True if the page is running over SSL
88          * @type Boolean
89          */
90         isSecure : isSecure,
91         /**
92          * True when the document is fully initialized and ready for action
93          * @type Boolean
94          */
95         isReady : false,
96         /**
97          * Turn on debugging output (currently only the factory uses this)
98          * @type Boolean
99          */
100         
101         debug: false,
102
103         /**
104          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
105          * @type Boolean
106          */
107         enableGarbageCollector : true,
108
109         /**
110          * True to automatically purge event listeners after uncaching an element (defaults to false).
111          * Note: this only happens if enableGarbageCollector is true.
112          * @type Boolean
113          */
114         enableListenerCollection:false,
115
116         /**
117          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
118          * the IE insecure content warning (defaults to javascript:false).
119          * @type String
120          */
121         SSL_SECURE_URL : "javascript:false",
122
123         /**
124          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
125          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
126          * @type String
127          */
128         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
129
130         emptyFn : function(){},
131         
132         /**
133          * Copies all the properties of config to obj if they don't already exist.
134          * @param {Object} obj The receiver of the properties
135          * @param {Object} config The source of the properties
136          * @return {Object} returns obj
137          */
138         applyIf : function(o, c){
139             if(o && c){
140                 for(var p in c){
141                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
142                 }
143             }
144             return o;
145         },
146
147         /**
148          * Applies event listeners to elements by selectors when the document is ready.
149          * The event name is specified with an @ suffix.
150 <pre><code>
151 Roo.addBehaviors({
152    // add a listener for click on all anchors in element with id foo
153    '#foo a@click' : function(e, t){
154        // do something
155    },
156
157    // add the same listener to multiple selectors (separated by comma BEFORE the @)
158    '#foo a, #bar span.some-class@mouseover' : function(){
159        // do something
160    }
161 });
162 </code></pre>
163          * @param {Object} obj The list of behaviors to apply
164          */
165         addBehaviors : function(o){
166             if(!Roo.isReady){
167                 Roo.onReady(function(){
168                     Roo.addBehaviors(o);
169                 });
170                 return;
171             }
172             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
173             for(var b in o){
174                 var parts = b.split('@');
175                 if(parts[1]){ // for Object prototype breakers
176                     var s = parts[0];
177                     if(!cache[s]){
178                         cache[s] = Roo.select(s);
179                     }
180                     cache[s].on(parts[1], o[b]);
181                 }
182             }
183             cache = null;
184         },
185
186         /**
187          * Generates unique ids. If the element already has an id, it is unchanged
188          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
189          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
190          * @return {String} The generated Id.
191          */
192         id : function(el, prefix){
193             prefix = prefix || "roo-gen";
194             el = Roo.getDom(el);
195             var id = prefix + (++idSeed);
196             return el ? (el.id ? el.id : (el.id = id)) : id;
197         },
198          
199        
200         /**
201          * Extends one class with another class and optionally overrides members with the passed literal. This class
202          * also adds the function "override()" to the class that can be used to override
203          * members on an instance.
204          * @param {Object} subclass The class inheriting the functionality
205          * @param {Object} superclass The class being extended
206          * @param {Object} overrides (optional) A literal with members
207          * @method extend
208          */
209         extend : function(){
210             // inline overrides
211             var io = function(o){
212                 for(var m in o){
213                     this[m] = o[m];
214                 }
215             };
216             return function(sb, sp, overrides){
217                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
218                     overrides = sp;
219                     sp = sb;
220                     sb = function(){sp.apply(this, arguments);};
221                 }
222                 var F = function(){}, sbp, spp = sp.prototype;
223                 F.prototype = spp;
224                 sbp = sb.prototype = new F();
225                 sbp.constructor=sb;
226                 sb.superclass=spp;
227                 
228                 if(spp.constructor == Object.prototype.constructor){
229                     spp.constructor=sp;
230                    
231                 }
232                 
233                 sb.override = function(o){
234                     Roo.override(sb, o);
235                 };
236                 sbp.override = io;
237                 Roo.override(sb, overrides);
238                 return sb;
239             };
240         }(),
241
242         /**
243          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
244          * Usage:<pre><code>
245 Roo.override(MyClass, {
246     newMethod1: function(){
247         // etc.
248     },
249     newMethod2: function(foo){
250         // etc.
251     }
252 });
253  </code></pre>
254          * @param {Object} origclass The class to override
255          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
256          * containing one or more methods.
257          * @method override
258          */
259         override : function(origclass, overrides){
260             if(overrides){
261                 var p = origclass.prototype;
262                 for(var method in overrides){
263                     p[method] = overrides[method];
264                 }
265             }
266         },
267         /**
268          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
269          * <pre><code>
270 Roo.namespace('Company', 'Company.data');
271 Company.Widget = function() { ... }
272 Company.data.CustomStore = function(config) { ... }
273 </code></pre>
274          * @param {String} namespace1
275          * @param {String} namespace2
276          * @param {String} etc
277          * @method namespace
278          */
279         namespace : function(){
280             var a=arguments, o=null, i, j, d, rt;
281             for (i=0; i<a.length; ++i) {
282                 d=a[i].split(".");
283                 rt = d[0];
284                 /** eval:var:o */
285                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
286                 for (j=1; j<d.length; ++j) {
287                     o[d[j]]=o[d[j]] || {};
288                     o=o[d[j]];
289                 }
290             }
291         },
292         /**
293          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
294          * <pre><code>
295 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
296 Roo.factory(conf, Roo.data);
297 </code></pre>
298          * @param {String} classname
299          * @param {String} namespace (optional)
300          * @method factory
301          */
302          
303         factory : function(c, ns)
304         {
305             // no xtype, no ns or c.xns - or forced off by c.xns
306             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
307                 return c;
308             }
309             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
310             if (c.constructor == ns[c.xtype]) {// already created...
311                 return c;
312             }
313             if (ns[c.xtype]) {
314                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
315                 var ret = new ns[c.xtype](c);
316                 ret.xns = false;
317                 return ret;
318             }
319             c.xns = false; // prevent recursion..
320             return c;
321         },
322          /**
323          * Logs to console if it can.
324          *
325          * @param {String|Object} string
326          * @method log
327          */
328         log : function(s)
329         {
330             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
331                 return; // alerT?
332             }
333             console.log(s);
334             
335         },
336         /**
337          * 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.
338          * @param {Object} o
339          * @return {String}
340          */
341         urlEncode : function(o){
342             if(!o){
343                 return "";
344             }
345             var buf = [];
346             for(var key in o){
347                 var ov = o[key], k = Roo.encodeURIComponent(key);
348                 var type = typeof ov;
349                 if(type == 'undefined'){
350                     buf.push(k, "=&");
351                 }else if(type != "function" && type != "object"){
352                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
353                 }else if(ov instanceof Array){
354                     if (ov.length) {
355                             for(var i = 0, len = ov.length; i < len; i++) {
356                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
357                             }
358                         } else {
359                             buf.push(k, "=&");
360                         }
361                 }
362             }
363             buf.pop();
364             return buf.join("");
365         },
366          /**
367          * Safe version of encodeURIComponent
368          * @param {String} data 
369          * @return {String} 
370          */
371         
372         encodeURIComponent : function (data)
373         {
374             try {
375                 return encodeURIComponent(data);
376             } catch(e) {} // should be an uri encode error.
377             
378             if (data == '' || data == null){
379                return '';
380             }
381             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
382             function nibble_to_hex(nibble){
383                 var chars = '0123456789ABCDEF';
384                 return chars.charAt(nibble);
385             }
386             data = data.toString();
387             var buffer = '';
388             for(var i=0; i<data.length; i++){
389                 var c = data.charCodeAt(i);
390                 var bs = new Array();
391                 if (c > 0x10000){
392                         // 4 bytes
393                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
394                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
395                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
396                     bs[3] = 0x80 | (c & 0x3F);
397                 }else if (c > 0x800){
398                          // 3 bytes
399                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
400                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
401                     bs[2] = 0x80 | (c & 0x3F);
402                 }else if (c > 0x80){
403                        // 2 bytes
404                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
405                     bs[1] = 0x80 | (c & 0x3F);
406                 }else{
407                         // 1 byte
408                     bs[0] = c;
409                 }
410                 for(var j=0; j<bs.length; j++){
411                     var b = bs[j];
412                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
413                             + nibble_to_hex(b &0x0F);
414                     buffer += '%'+hex;
415                }
416             }
417             return buffer;    
418              
419         },
420
421         /**
422          * 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]}.
423          * @param {String} string
424          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
425          * @return {Object} A literal with members
426          */
427         urlDecode : function(string, overwrite){
428             if(!string || !string.length){
429                 return {};
430             }
431             var obj = {};
432             var pairs = string.split('&');
433             var pair, name, value;
434             for(var i = 0, len = pairs.length; i < len; i++){
435                 pair = pairs[i].split('=');
436                 name = decodeURIComponent(pair[0]);
437                 value = decodeURIComponent(pair[1]);
438                 if(overwrite !== true){
439                     if(typeof obj[name] == "undefined"){
440                         obj[name] = value;
441                     }else if(typeof obj[name] == "string"){
442                         obj[name] = [obj[name]];
443                         obj[name].push(value);
444                     }else{
445                         obj[name].push(value);
446                     }
447                 }else{
448                     obj[name] = value;
449                 }
450             }
451             return obj;
452         },
453
454         /**
455          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
456          * passed array is not really an array, your function is called once with it.
457          * The supplied function is called with (Object item, Number index, Array allItems).
458          * @param {Array/NodeList/Mixed} array
459          * @param {Function} fn
460          * @param {Object} scope
461          */
462         each : function(array, fn, scope){
463             if(typeof array.length == "undefined" || typeof array == "string"){
464                 array = [array];
465             }
466             for(var i = 0, len = array.length; i < len; i++){
467                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
468             }
469         },
470
471         // deprecated
472         combine : function(){
473             var as = arguments, l = as.length, r = [];
474             for(var i = 0; i < l; i++){
475                 var a = as[i];
476                 if(a instanceof Array){
477                     r = r.concat(a);
478                 }else if(a.length !== undefined && !a.substr){
479                     r = r.concat(Array.prototype.slice.call(a, 0));
480                 }else{
481                     r.push(a);
482                 }
483             }
484             return r;
485         },
486
487         /**
488          * Escapes the passed string for use in a regular expression
489          * @param {String} str
490          * @return {String}
491          */
492         escapeRe : function(s) {
493             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
494         },
495
496         // internal
497         callback : function(cb, scope, args, delay){
498             if(typeof cb == "function"){
499                 if(delay){
500                     cb.defer(delay, scope, args || []);
501                 }else{
502                     cb.apply(scope, args || []);
503                 }
504             }
505         },
506
507         /**
508          * Return the dom node for the passed string (id), dom node, or Roo.Element
509          * @param {String/HTMLElement/Roo.Element} el
510          * @return HTMLElement
511          */
512         getDom : function(el){
513             if(!el){
514                 return null;
515             }
516             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
517         },
518
519         /**
520         * Shorthand for {@link Roo.ComponentMgr#get}
521         * @param {String} id
522         * @return Roo.Component
523         */
524         getCmp : function(id){
525             return Roo.ComponentMgr.get(id);
526         },
527          
528         num : function(v, defaultValue){
529             if(typeof v != 'number'){
530                 return defaultValue;
531             }
532             return v;
533         },
534
535         destroy : function(){
536             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
537                 var as = a[i];
538                 if(as){
539                     if(as.dom){
540                         as.removeAllListeners();
541                         as.remove();
542                         continue;
543                     }
544                     if(typeof as.purgeListeners == 'function'){
545                         as.purgeListeners();
546                     }
547                     if(typeof as.destroy == 'function'){
548                         as.destroy();
549                     }
550                 }
551             }
552         },
553
554         // inpired by a similar function in mootools library
555         /**
556          * Returns the type of object that is passed in. If the object passed in is null or undefined it
557          * return false otherwise it returns one of the following values:<ul>
558          * <li><b>string</b>: If the object passed is a string</li>
559          * <li><b>number</b>: If the object passed is a number</li>
560          * <li><b>boolean</b>: If the object passed is a boolean value</li>
561          * <li><b>function</b>: If the object passed is a function reference</li>
562          * <li><b>object</b>: If the object passed is an object</li>
563          * <li><b>array</b>: If the object passed is an array</li>
564          * <li><b>regexp</b>: If the object passed is a regular expression</li>
565          * <li><b>element</b>: If the object passed is a DOM Element</li>
566          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
567          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
568          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
569          * @param {Mixed} object
570          * @return {String}
571          */
572         type : function(o){
573             if(o === undefined || o === null){
574                 return false;
575             }
576             if(o.htmlElement){
577                 return 'element';
578             }
579             var t = typeof o;
580             if(t == 'object' && o.nodeName) {
581                 switch(o.nodeType) {
582                     case 1: return 'element';
583                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
584                 }
585             }
586             if(t == 'object' || t == 'function') {
587                 switch(o.constructor) {
588                     case Array: return 'array';
589                     case RegExp: return 'regexp';
590                 }
591                 if(typeof o.length == 'number' && typeof o.item == 'function') {
592                     return 'nodelist';
593                 }
594             }
595             return t;
596         },
597
598         /**
599          * Returns true if the passed value is null, undefined or an empty string (optional).
600          * @param {Mixed} value The value to test
601          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
602          * @return {Boolean}
603          */
604         isEmpty : function(v, allowBlank){
605             return v === null || v === undefined || (!allowBlank ? v === '' : false);
606         },
607         
608         /** @type Boolean */
609         isOpera : isOpera,
610         /** @type Boolean */
611         isSafari : isSafari,
612         /** @type Boolean */
613         isIE : isIE,
614         /** @type Boolean */
615         isIE7 : isIE7,
616         /** @type Boolean */
617         isGecko : isGecko,
618         /** @type Boolean */
619         isBorderBox : isBorderBox,
620         /** @type Boolean */
621         isWindows : isWindows,
622         /** @type Boolean */
623         isLinux : isLinux,
624         /** @type Boolean */
625         isMac : isMac,
626         /** @type Boolean */
627         isTouch : isTouch,
628
629         /**
630          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
631          * you may want to set this to true.
632          * @type Boolean
633          */
634         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
635         
636         
637                 
638         /**
639          * Selects a single element as a Roo Element
640          * This is about as close as you can get to jQuery's $('do crazy stuff')
641          * @param {String} selector The selector/xpath query
642          * @param {Node} root (optional) The start of the query (defaults to document).
643          * @return {Roo.Element}
644          */
645         selectNode : function(selector, root) 
646         {
647             var node = Roo.DomQuery.selectNode(selector,root);
648             return node ? Roo.get(node) : new Roo.Element(false);
649         }
650         
651     });
652
653
654 })();
655
656 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
657                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
658                 "Roo.app", "Roo.ux",
659                 "Roo.bootstrap",
660                 "Roo.bootstrap.dash");
661 /*
662  * Based on:
663  * Ext JS Library 1.1.1
664  * Copyright(c) 2006-2007, Ext JS, LLC.
665  *
666  * Originally Released Under LGPL - original licence link has changed is not relivant.
667  *
668  * Fork - LGPL
669  * <script type="text/javascript">
670  */
671
672 (function() {    
673     // wrappedn so fnCleanup is not in global scope...
674     if(Roo.isIE) {
675         function fnCleanUp() {
676             var p = Function.prototype;
677             delete p.createSequence;
678             delete p.defer;
679             delete p.createDelegate;
680             delete p.createCallback;
681             delete p.createInterceptor;
682
683             window.detachEvent("onunload", fnCleanUp);
684         }
685         window.attachEvent("onunload", fnCleanUp);
686     }
687 })();
688
689
690 /**
691  * @class Function
692  * These functions are available on every Function object (any JavaScript function).
693  */
694 Roo.apply(Function.prototype, {
695      /**
696      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
697      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
698      * Will create a function that is bound to those 2 args.
699      * @return {Function} The new function
700     */
701     createCallback : function(/*args...*/){
702         // make args available, in function below
703         var args = arguments;
704         var method = this;
705         return function() {
706             return method.apply(window, args);
707         };
708     },
709
710     /**
711      * Creates a delegate (callback) that sets the scope to obj.
712      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
713      * Will create a function that is automatically scoped to this.
714      * @param {Object} obj (optional) The object for which the scope is set
715      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
716      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
717      *                                             if a number the args are inserted at the specified position
718      * @return {Function} The new function
719      */
720     createDelegate : function(obj, args, appendArgs){
721         var method = this;
722         return function() {
723             var callArgs = args || arguments;
724             if(appendArgs === true){
725                 callArgs = Array.prototype.slice.call(arguments, 0);
726                 callArgs = callArgs.concat(args);
727             }else if(typeof appendArgs == "number"){
728                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
729                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
730                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
731             }
732             return method.apply(obj || window, callArgs);
733         };
734     },
735
736     /**
737      * Calls this function after the number of millseconds specified.
738      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
739      * @param {Object} obj (optional) The object for which the scope is set
740      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
741      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
742      *                                             if a number the args are inserted at the specified position
743      * @return {Number} The timeout id that can be used with clearTimeout
744      */
745     defer : function(millis, obj, args, appendArgs){
746         var fn = this.createDelegate(obj, args, appendArgs);
747         if(millis){
748             return setTimeout(fn, millis);
749         }
750         fn();
751         return 0;
752     },
753     /**
754      * Create a combined function call sequence of the original function + the passed function.
755      * The resulting function returns the results of the original function.
756      * The passed fcn is called with the parameters of the original function
757      * @param {Function} fcn The function to sequence
758      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
759      * @return {Function} The new function
760      */
761     createSequence : function(fcn, scope){
762         if(typeof fcn != "function"){
763             return this;
764         }
765         var method = this;
766         return function() {
767             var retval = method.apply(this || window, arguments);
768             fcn.apply(scope || this || window, arguments);
769             return retval;
770         };
771     },
772
773     /**
774      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
775      * The resulting function returns the results of the original function.
776      * The passed fcn is called with the parameters of the original function.
777      * @addon
778      * @param {Function} fcn The function to call before the original
779      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
780      * @return {Function} The new function
781      */
782     createInterceptor : function(fcn, scope){
783         if(typeof fcn != "function"){
784             return this;
785         }
786         var method = this;
787         return function() {
788             fcn.target = this;
789             fcn.method = method;
790             if(fcn.apply(scope || this || window, arguments) === false){
791                 return;
792             }
793             return method.apply(this || window, arguments);
794         };
795     }
796 });
797 /*
798  * Based on:
799  * Ext JS Library 1.1.1
800  * Copyright(c) 2006-2007, Ext JS, LLC.
801  *
802  * Originally Released Under LGPL - original licence link has changed is not relivant.
803  *
804  * Fork - LGPL
805  * <script type="text/javascript">
806  */
807
808 Roo.applyIf(String, {
809     
810     /** @scope String */
811     
812     /**
813      * Escapes the passed string for ' and \
814      * @param {String} string The string to escape
815      * @return {String} The escaped string
816      * @static
817      */
818     escape : function(string) {
819         return string.replace(/('|\\)/g, "\\$1");
820     },
821
822     /**
823      * Pads the left side of a string with a specified character.  This is especially useful
824      * for normalizing number and date strings.  Example usage:
825      * <pre><code>
826 var s = String.leftPad('123', 5, '0');
827 // s now contains the string: '00123'
828 </code></pre>
829      * @param {String} string The original string
830      * @param {Number} size The total length of the output string
831      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
832      * @return {String} The padded string
833      * @static
834      */
835     leftPad : function (val, size, ch) {
836         var result = new String(val);
837         if(ch === null || ch === undefined || ch === '') {
838             ch = " ";
839         }
840         while (result.length < size) {
841             result = ch + result;
842         }
843         return result;
844     },
845
846     /**
847      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
848      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
849      * <pre><code>
850 var cls = 'my-class', text = 'Some text';
851 var s = String.format('<div class="{0}">{1}</div>', cls, text);
852 // s now contains the string: '<div class="my-class">Some text</div>'
853 </code></pre>
854      * @param {String} string The tokenized string to be formatted
855      * @param {String} value1 The value to replace token {0}
856      * @param {String} value2 Etc...
857      * @return {String} The formatted string
858      * @static
859      */
860     format : function(format){
861         var args = Array.prototype.slice.call(arguments, 1);
862         return format.replace(/\{(\d+)\}/g, function(m, i){
863             return Roo.util.Format.htmlEncode(args[i]);
864         });
865     }
866 });
867
868 /**
869  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
870  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
871  * they are already different, the first value passed in is returned.  Note that this method returns the new value
872  * but does not change the current string.
873  * <pre><code>
874 // alternate sort directions
875 sort = sort.toggle('ASC', 'DESC');
876
877 // instead of conditional logic:
878 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
879 </code></pre>
880  * @param {String} value The value to compare to the current string
881  * @param {String} other The new value to use if the string already equals the first value passed in
882  * @return {String} The new value
883  */
884  
885 String.prototype.toggle = function(value, other){
886     return this == value ? other : value;
887 };/*
888  * Based on:
889  * Ext JS Library 1.1.1
890  * Copyright(c) 2006-2007, Ext JS, LLC.
891  *
892  * Originally Released Under LGPL - original licence link has changed is not relivant.
893  *
894  * Fork - LGPL
895  * <script type="text/javascript">
896  */
897
898  /**
899  * @class Number
900  */
901 Roo.applyIf(Number.prototype, {
902     /**
903      * Checks whether or not the current number is within a desired range.  If the number is already within the
904      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
905      * exceeded.  Note that this method returns the constrained value but does not change the current number.
906      * @param {Number} min The minimum number in the range
907      * @param {Number} max The maximum number in the range
908      * @return {Number} The constrained value if outside the range, otherwise the current value
909      */
910     constrain : function(min, max){
911         return Math.min(Math.max(this, min), max);
912     }
913 });/*
914  * Based on:
915  * Ext JS Library 1.1.1
916  * Copyright(c) 2006-2007, Ext JS, LLC.
917  *
918  * Originally Released Under LGPL - original licence link has changed is not relivant.
919  *
920  * Fork - LGPL
921  * <script type="text/javascript">
922  */
923  /**
924  * @class Array
925  */
926 Roo.applyIf(Array.prototype, {
927     /**
928      * 
929      * Checks whether or not the specified object exists in the array.
930      * @param {Object} o The object to check for
931      * @return {Number} The index of o in the array (or -1 if it is not found)
932      */
933     indexOf : function(o){
934        for (var i = 0, len = this.length; i < len; i++){
935               if(this[i] == o) return i;
936        }
937            return -1;
938     },
939
940     /**
941      * Removes the specified object from the array.  If the object is not found nothing happens.
942      * @param {Object} o The object to remove
943      */
944     remove : function(o){
945        var index = this.indexOf(o);
946        if(index != -1){
947            this.splice(index, 1);
948        }
949     },
950     /**
951      * Map (JS 1.6 compatibility)
952      * @param {Function} function  to call
953      */
954     map : function(fun )
955     {
956         var len = this.length >>> 0;
957         if (typeof fun != "function")
958             throw new TypeError();
959
960         var res = new Array(len);
961         var thisp = arguments[1];
962         for (var i = 0; i < len; i++)
963         {
964             if (i in this)
965                 res[i] = fun.call(thisp, this[i], i, this);
966         }
967
968         return res;
969     }
970     
971 });
972
973
974  /*
975  * Based on:
976  * Ext JS Library 1.1.1
977  * Copyright(c) 2006-2007, Ext JS, LLC.
978  *
979  * Originally Released Under LGPL - original licence link has changed is not relivant.
980  *
981  * Fork - LGPL
982  * <script type="text/javascript">
983  */
984
985 /**
986  * @class Date
987  *
988  * The date parsing and format syntax is a subset of
989  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
990  * supported will provide results equivalent to their PHP versions.
991  *
992  * Following is the list of all currently supported formats:
993  *<pre>
994 Sample date:
995 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
996
997 Format  Output      Description
998 ------  ----------  --------------------------------------------------------------
999   d      10         Day of the month, 2 digits with leading zeros
1000   D      Wed        A textual representation of a day, three letters
1001   j      10         Day of the month without leading zeros
1002   l      Wednesday  A full textual representation of the day of the week
1003   S      th         English ordinal day of month suffix, 2 chars (use with j)
1004   w      3          Numeric representation of the day of the week
1005   z      9          The julian date, or day of the year (0-365)
1006   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1007   F      January    A full textual representation of the month
1008   m      01         Numeric representation of a month, with leading zeros
1009   M      Jan        Month name abbreviation, three letters
1010   n      1          Numeric representation of a month, without leading zeros
1011   t      31         Number of days in the given month
1012   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1013   Y      2007       A full numeric representation of a year, 4 digits
1014   y      07         A two digit representation of a year
1015   a      pm         Lowercase Ante meridiem and Post meridiem
1016   A      PM         Uppercase Ante meridiem and Post meridiem
1017   g      3          12-hour format of an hour without leading zeros
1018   G      15         24-hour format of an hour without leading zeros
1019   h      03         12-hour format of an hour with leading zeros
1020   H      15         24-hour format of an hour with leading zeros
1021   i      05         Minutes with leading zeros
1022   s      01         Seconds, with leading zeros
1023   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1024   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1025   T      CST        Timezone setting of the machine running the code
1026   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1027 </pre>
1028  *
1029  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1030  * <pre><code>
1031 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1032 document.write(dt.format('Y-m-d'));                         //2007-01-10
1033 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1034 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
1035  </code></pre>
1036  *
1037  * Here are some standard date/time patterns that you might find helpful.  They
1038  * are not part of the source of Date.js, but to use them you can simply copy this
1039  * block of code into any script that is included after Date.js and they will also become
1040  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1041  * <pre><code>
1042 Date.patterns = {
1043     ISO8601Long:"Y-m-d H:i:s",
1044     ISO8601Short:"Y-m-d",
1045     ShortDate: "n/j/Y",
1046     LongDate: "l, F d, Y",
1047     FullDateTime: "l, F d, Y g:i:s A",
1048     MonthDay: "F d",
1049     ShortTime: "g:i A",
1050     LongTime: "g:i:s A",
1051     SortableDateTime: "Y-m-d\\TH:i:s",
1052     UniversalSortableDateTime: "Y-m-d H:i:sO",
1053     YearMonth: "F, Y"
1054 };
1055 </code></pre>
1056  *
1057  * Example usage:
1058  * <pre><code>
1059 var dt = new Date();
1060 document.write(dt.format(Date.patterns.ShortDate));
1061  </code></pre>
1062  */
1063
1064 /*
1065  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1066  * They generate precompiled functions from date formats instead of parsing and
1067  * processing the pattern every time you format a date.  These functions are available
1068  * on every Date object (any javascript function).
1069  *
1070  * The original article and download are here:
1071  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1072  *
1073  */
1074  
1075  
1076  // was in core
1077 /**
1078  Returns the number of milliseconds between this date and date
1079  @param {Date} date (optional) Defaults to now
1080  @return {Number} The diff in milliseconds
1081  @member Date getElapsed
1082  */
1083 Date.prototype.getElapsed = function(date) {
1084         return Math.abs((date || new Date()).getTime()-this.getTime());
1085 };
1086 // was in date file..
1087
1088
1089 // private
1090 Date.parseFunctions = {count:0};
1091 // private
1092 Date.parseRegexes = [];
1093 // private
1094 Date.formatFunctions = {count:0};
1095
1096 // private
1097 Date.prototype.dateFormat = function(format) {
1098     if (Date.formatFunctions[format] == null) {
1099         Date.createNewFormat(format);
1100     }
1101     var func = Date.formatFunctions[format];
1102     return this[func]();
1103 };
1104
1105
1106 /**
1107  * Formats a date given the supplied format string
1108  * @param {String} format The format string
1109  * @return {String} The formatted date
1110  * @method
1111  */
1112 Date.prototype.format = Date.prototype.dateFormat;
1113
1114 // private
1115 Date.createNewFormat = function(format) {
1116     var funcName = "format" + Date.formatFunctions.count++;
1117     Date.formatFunctions[format] = funcName;
1118     var code = "Date.prototype." + funcName + " = function(){return ";
1119     var special = false;
1120     var ch = '';
1121     for (var i = 0; i < format.length; ++i) {
1122         ch = format.charAt(i);
1123         if (!special && ch == "\\") {
1124             special = true;
1125         }
1126         else if (special) {
1127             special = false;
1128             code += "'" + String.escape(ch) + "' + ";
1129         }
1130         else {
1131             code += Date.getFormatCode(ch);
1132         }
1133     }
1134     /** eval:var:zzzzzzzzzzzzz */
1135     eval(code.substring(0, code.length - 3) + ";}");
1136 };
1137
1138 // private
1139 Date.getFormatCode = function(character) {
1140     switch (character) {
1141     case "d":
1142         return "String.leftPad(this.getDate(), 2, '0') + ";
1143     case "D":
1144         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1145     case "j":
1146         return "this.getDate() + ";
1147     case "l":
1148         return "Date.dayNames[this.getDay()] + ";
1149     case "S":
1150         return "this.getSuffix() + ";
1151     case "w":
1152         return "this.getDay() + ";
1153     case "z":
1154         return "this.getDayOfYear() + ";
1155     case "W":
1156         return "this.getWeekOfYear() + ";
1157     case "F":
1158         return "Date.monthNames[this.getMonth()] + ";
1159     case "m":
1160         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1161     case "M":
1162         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1163     case "n":
1164         return "(this.getMonth() + 1) + ";
1165     case "t":
1166         return "this.getDaysInMonth() + ";
1167     case "L":
1168         return "(this.isLeapYear() ? 1 : 0) + ";
1169     case "Y":
1170         return "this.getFullYear() + ";
1171     case "y":
1172         return "('' + this.getFullYear()).substring(2, 4) + ";
1173     case "a":
1174         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1175     case "A":
1176         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1177     case "g":
1178         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1179     case "G":
1180         return "this.getHours() + ";
1181     case "h":
1182         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1183     case "H":
1184         return "String.leftPad(this.getHours(), 2, '0') + ";
1185     case "i":
1186         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1187     case "s":
1188         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1189     case "O":
1190         return "this.getGMTOffset() + ";
1191     case "P":
1192         return "this.getGMTColonOffset() + ";
1193     case "T":
1194         return "this.getTimezone() + ";
1195     case "Z":
1196         return "(this.getTimezoneOffset() * -60) + ";
1197     default:
1198         return "'" + String.escape(character) + "' + ";
1199     }
1200 };
1201
1202 /**
1203  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1204  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1205  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1206  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1207  * string or the parse operation will fail.
1208  * Example Usage:
1209 <pre><code>
1210 //dt = Fri May 25 2007 (current date)
1211 var dt = new Date();
1212
1213 //dt = Thu May 25 2006 (today's month/day in 2006)
1214 dt = Date.parseDate("2006", "Y");
1215
1216 //dt = Sun Jan 15 2006 (all date parts specified)
1217 dt = Date.parseDate("2006-1-15", "Y-m-d");
1218
1219 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1220 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1221 </code></pre>
1222  * @param {String} input The unparsed date as a string
1223  * @param {String} format The format the date is in
1224  * @return {Date} The parsed date
1225  * @static
1226  */
1227 Date.parseDate = function(input, format) {
1228     if (Date.parseFunctions[format] == null) {
1229         Date.createParser(format);
1230     }
1231     var func = Date.parseFunctions[format];
1232     return Date[func](input);
1233 };
1234 /**
1235  * @private
1236  */
1237
1238 Date.createParser = function(format) {
1239     var funcName = "parse" + Date.parseFunctions.count++;
1240     var regexNum = Date.parseRegexes.length;
1241     var currentGroup = 1;
1242     Date.parseFunctions[format] = funcName;
1243
1244     var code = "Date." + funcName + " = function(input){\n"
1245         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1246         + "var d = new Date();\n"
1247         + "y = d.getFullYear();\n"
1248         + "m = d.getMonth();\n"
1249         + "d = d.getDate();\n"
1250         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1251         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1252         + "if (results && results.length > 0) {";
1253     var regex = "";
1254
1255     var special = false;
1256     var ch = '';
1257     for (var i = 0; i < format.length; ++i) {
1258         ch = format.charAt(i);
1259         if (!special && ch == "\\") {
1260             special = true;
1261         }
1262         else if (special) {
1263             special = false;
1264             regex += String.escape(ch);
1265         }
1266         else {
1267             var obj = Date.formatCodeToRegex(ch, currentGroup);
1268             currentGroup += obj.g;
1269             regex += obj.s;
1270             if (obj.g && obj.c) {
1271                 code += obj.c;
1272             }
1273         }
1274     }
1275
1276     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1277         + "{v = new Date(y, m, d, h, i, s);}\n"
1278         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1279         + "{v = new Date(y, m, d, h, i);}\n"
1280         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1281         + "{v = new Date(y, m, d, h);}\n"
1282         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1283         + "{v = new Date(y, m, d);}\n"
1284         + "else if (y >= 0 && m >= 0)\n"
1285         + "{v = new Date(y, m);}\n"
1286         + "else if (y >= 0)\n"
1287         + "{v = new Date(y);}\n"
1288         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1289         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1290         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1291         + ";}";
1292
1293     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1294     /** eval:var:zzzzzzzzzzzzz */
1295     eval(code);
1296 };
1297
1298 // private
1299 Date.formatCodeToRegex = function(character, currentGroup) {
1300     switch (character) {
1301     case "D":
1302         return {g:0,
1303         c:null,
1304         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1305     case "j":
1306         return {g:1,
1307             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1308             s:"(\\d{1,2})"}; // day of month without leading zeroes
1309     case "d":
1310         return {g:1,
1311             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1312             s:"(\\d{2})"}; // day of month with leading zeroes
1313     case "l":
1314         return {g:0,
1315             c:null,
1316             s:"(?:" + Date.dayNames.join("|") + ")"};
1317     case "S":
1318         return {g:0,
1319             c:null,
1320             s:"(?:st|nd|rd|th)"};
1321     case "w":
1322         return {g:0,
1323             c:null,
1324             s:"\\d"};
1325     case "z":
1326         return {g:0,
1327             c:null,
1328             s:"(?:\\d{1,3})"};
1329     case "W":
1330         return {g:0,
1331             c:null,
1332             s:"(?:\\d{2})"};
1333     case "F":
1334         return {g:1,
1335             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1336             s:"(" + Date.monthNames.join("|") + ")"};
1337     case "M":
1338         return {g:1,
1339             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1340             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1341     case "n":
1342         return {g:1,
1343             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1344             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1345     case "m":
1346         return {g:1,
1347             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1348             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1349     case "t":
1350         return {g:0,
1351             c:null,
1352             s:"\\d{1,2}"};
1353     case "L":
1354         return {g:0,
1355             c:null,
1356             s:"(?:1|0)"};
1357     case "Y":
1358         return {g:1,
1359             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1360             s:"(\\d{4})"};
1361     case "y":
1362         return {g:1,
1363             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1364                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1365             s:"(\\d{1,2})"};
1366     case "a":
1367         return {g:1,
1368             c:"if (results[" + currentGroup + "] == 'am') {\n"
1369                 + "if (h == 12) { h = 0; }\n"
1370                 + "} else { if (h < 12) { h += 12; }}",
1371             s:"(am|pm)"};
1372     case "A":
1373         return {g:1,
1374             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1375                 + "if (h == 12) { h = 0; }\n"
1376                 + "} else { if (h < 12) { h += 12; }}",
1377             s:"(AM|PM)"};
1378     case "g":
1379     case "G":
1380         return {g:1,
1381             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1382             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1383     case "h":
1384     case "H":
1385         return {g:1,
1386             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1387             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1388     case "i":
1389         return {g:1,
1390             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{2})"};
1392     case "s":
1393         return {g:1,
1394             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1395             s:"(\\d{2})"};
1396     case "O":
1397         return {g:1,
1398             c:[
1399                 "o = results[", currentGroup, "];\n",
1400                 "var sn = o.substring(0,1);\n", // get + / - sign
1401                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1402                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1403                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1404                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1405             ].join(""),
1406             s:"([+\-]\\d{2,4})"};
1407     
1408     
1409     case "P":
1410         return {g:1,
1411                 c:[
1412                    "o = results[", currentGroup, "];\n",
1413                    "var sn = o.substring(0,1);\n",
1414                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1415                    "var mn = o.substring(4,6) % 60;\n",
1416                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1417                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1418             ].join(""),
1419             s:"([+\-]\\d{4})"};
1420     case "T":
1421         return {g:0,
1422             c:null,
1423             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1424     case "Z":
1425         return {g:1,
1426             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1427                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1428             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1429     default:
1430         return {g:0,
1431             c:null,
1432             s:String.escape(character)};
1433     }
1434 };
1435
1436 /**
1437  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1438  * @return {String} The abbreviated timezone name (e.g. 'CST')
1439  */
1440 Date.prototype.getTimezone = function() {
1441     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1442 };
1443
1444 /**
1445  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1446  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1447  */
1448 Date.prototype.getGMTOffset = function() {
1449     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1452 };
1453
1454 /**
1455  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1456  * @return {String} 2-characters representing hours and 2-characters representing minutes
1457  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1458  */
1459 Date.prototype.getGMTColonOffset = function() {
1460         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1461                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1462                 + ":"
1463                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1464 }
1465
1466 /**
1467  * Get the numeric day number of the year, adjusted for leap year.
1468  * @return {Number} 0 through 364 (365 in leap years)
1469  */
1470 Date.prototype.getDayOfYear = function() {
1471     var num = 0;
1472     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1473     for (var i = 0; i < this.getMonth(); ++i) {
1474         num += Date.daysInMonth[i];
1475     }
1476     return num + this.getDate() - 1;
1477 };
1478
1479 /**
1480  * Get the string representation of the numeric week number of the year
1481  * (equivalent to the format specifier 'W').
1482  * @return {String} '00' through '52'
1483  */
1484 Date.prototype.getWeekOfYear = function() {
1485     // Skip to Thursday of this week
1486     var now = this.getDayOfYear() + (4 - this.getDay());
1487     // Find the first Thursday of the year
1488     var jan1 = new Date(this.getFullYear(), 0, 1);
1489     var then = (7 - jan1.getDay() + 4);
1490     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1491 };
1492
1493 /**
1494  * Whether or not the current date is in a leap year.
1495  * @return {Boolean} True if the current date is in a leap year, else false
1496  */
1497 Date.prototype.isLeapYear = function() {
1498     var year = this.getFullYear();
1499     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1500 };
1501
1502 /**
1503  * Get the first day of the current month, adjusted for leap year.  The returned value
1504  * is the numeric day index within the week (0-6) which can be used in conjunction with
1505  * the {@link #monthNames} array to retrieve the textual day name.
1506  * Example:
1507  *<pre><code>
1508 var dt = new Date('1/10/2007');
1509 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1510 </code></pre>
1511  * @return {Number} The day number (0-6)
1512  */
1513 Date.prototype.getFirstDayOfMonth = function() {
1514     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1515     return (day < 0) ? (day + 7) : day;
1516 };
1517
1518 /**
1519  * Get the last day of the current month, adjusted for leap year.  The returned value
1520  * is the numeric day index within the week (0-6) which can be used in conjunction with
1521  * the {@link #monthNames} array to retrieve the textual day name.
1522  * Example:
1523  *<pre><code>
1524 var dt = new Date('1/10/2007');
1525 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1526 </code></pre>
1527  * @return {Number} The day number (0-6)
1528  */
1529 Date.prototype.getLastDayOfMonth = function() {
1530     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1531     return (day < 0) ? (day + 7) : day;
1532 };
1533
1534
1535 /**
1536  * Get the first date of this date's month
1537  * @return {Date}
1538  */
1539 Date.prototype.getFirstDateOfMonth = function() {
1540     return new Date(this.getFullYear(), this.getMonth(), 1);
1541 };
1542
1543 /**
1544  * Get the last date of this date's month
1545  * @return {Date}
1546  */
1547 Date.prototype.getLastDateOfMonth = function() {
1548     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1549 };
1550 /**
1551  * Get the number of days in the current month, adjusted for leap year.
1552  * @return {Number} The number of days in the month
1553  */
1554 Date.prototype.getDaysInMonth = function() {
1555     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1556     return Date.daysInMonth[this.getMonth()];
1557 };
1558
1559 /**
1560  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1561  * @return {String} 'st, 'nd', 'rd' or 'th'
1562  */
1563 Date.prototype.getSuffix = function() {
1564     switch (this.getDate()) {
1565         case 1:
1566         case 21:
1567         case 31:
1568             return "st";
1569         case 2:
1570         case 22:
1571             return "nd";
1572         case 3:
1573         case 23:
1574             return "rd";
1575         default:
1576             return "th";
1577     }
1578 };
1579
1580 // private
1581 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1582
1583 /**
1584  * An array of textual month names.
1585  * Override these values for international dates, for example...
1586  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1587  * @type Array
1588  * @static
1589  */
1590 Date.monthNames =
1591    ["January",
1592     "February",
1593     "March",
1594     "April",
1595     "May",
1596     "June",
1597     "July",
1598     "August",
1599     "September",
1600     "October",
1601     "November",
1602     "December"];
1603
1604 /**
1605  * An array of textual day names.
1606  * Override these values for international dates, for example...
1607  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1608  * @type Array
1609  * @static
1610  */
1611 Date.dayNames =
1612    ["Sunday",
1613     "Monday",
1614     "Tuesday",
1615     "Wednesday",
1616     "Thursday",
1617     "Friday",
1618     "Saturday"];
1619
1620 // private
1621 Date.y2kYear = 50;
1622 // private
1623 Date.monthNumbers = {
1624     Jan:0,
1625     Feb:1,
1626     Mar:2,
1627     Apr:3,
1628     May:4,
1629     Jun:5,
1630     Jul:6,
1631     Aug:7,
1632     Sep:8,
1633     Oct:9,
1634     Nov:10,
1635     Dec:11};
1636
1637 /**
1638  * Creates and returns a new Date instance with the exact same date value as the called instance.
1639  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1640  * variable will also be changed.  When the intention is to create a new variable that will not
1641  * modify the original instance, you should create a clone.
1642  *
1643  * Example of correctly cloning a date:
1644  * <pre><code>
1645 //wrong way:
1646 var orig = new Date('10/1/2006');
1647 var copy = orig;
1648 copy.setDate(5);
1649 document.write(orig);  //returns 'Thu Oct 05 2006'!
1650
1651 //correct way:
1652 var orig = new Date('10/1/2006');
1653 var copy = orig.clone();
1654 copy.setDate(5);
1655 document.write(orig);  //returns 'Thu Oct 01 2006'
1656 </code></pre>
1657  * @return {Date} The new Date instance
1658  */
1659 Date.prototype.clone = function() {
1660         return new Date(this.getTime());
1661 };
1662
1663 /**
1664  * Clears any time information from this date
1665  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1666  @return {Date} this or the clone
1667  */
1668 Date.prototype.clearTime = function(clone){
1669     if(clone){
1670         return this.clone().clearTime();
1671     }
1672     this.setHours(0);
1673     this.setMinutes(0);
1674     this.setSeconds(0);
1675     this.setMilliseconds(0);
1676     return this;
1677 };
1678
1679 // private
1680 // safari setMonth is broken
1681 if(Roo.isSafari){
1682     Date.brokenSetMonth = Date.prototype.setMonth;
1683         Date.prototype.setMonth = function(num){
1684                 if(num <= -1){
1685                         var n = Math.ceil(-num);
1686                         var back_year = Math.ceil(n/12);
1687                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1688                         this.setFullYear(this.getFullYear() - back_year);
1689                         return Date.brokenSetMonth.call(this, month);
1690                 } else {
1691                         return Date.brokenSetMonth.apply(this, arguments);
1692                 }
1693         };
1694 }
1695
1696 /** Date interval constant 
1697 * @static 
1698 * @type String */
1699 Date.MILLI = "ms";
1700 /** Date interval constant 
1701 * @static 
1702 * @type String */
1703 Date.SECOND = "s";
1704 /** Date interval constant 
1705 * @static 
1706 * @type String */
1707 Date.MINUTE = "mi";
1708 /** Date interval constant 
1709 * @static 
1710 * @type String */
1711 Date.HOUR = "h";
1712 /** Date interval constant 
1713 * @static 
1714 * @type String */
1715 Date.DAY = "d";
1716 /** Date interval constant 
1717 * @static 
1718 * @type String */
1719 Date.MONTH = "mo";
1720 /** Date interval constant 
1721 * @static 
1722 * @type String */
1723 Date.YEAR = "y";
1724
1725 /**
1726  * Provides a convenient method of performing basic date arithmetic.  This method
1727  * does not modify the Date instance being called - it creates and returns
1728  * a new Date instance containing the resulting date value.
1729  *
1730  * Examples:
1731  * <pre><code>
1732 //Basic usage:
1733 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1734 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1735
1736 //Negative values will subtract correctly:
1737 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1738 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1739
1740 //You can even chain several calls together in one line!
1741 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1742 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1743  </code></pre>
1744  *
1745  * @param {String} interval   A valid date interval enum value
1746  * @param {Number} value      The amount to add to the current date
1747  * @return {Date} The new Date instance
1748  */
1749 Date.prototype.add = function(interval, value){
1750   var d = this.clone();
1751   if (!interval || value === 0) return d;
1752   switch(interval.toLowerCase()){
1753     case Date.MILLI:
1754       d.setMilliseconds(this.getMilliseconds() + value);
1755       break;
1756     case Date.SECOND:
1757       d.setSeconds(this.getSeconds() + value);
1758       break;
1759     case Date.MINUTE:
1760       d.setMinutes(this.getMinutes() + value);
1761       break;
1762     case Date.HOUR:
1763       d.setHours(this.getHours() + value);
1764       break;
1765     case Date.DAY:
1766       d.setDate(this.getDate() + value);
1767       break;
1768     case Date.MONTH:
1769       var day = this.getDate();
1770       if(day > 28){
1771           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1772       }
1773       d.setDate(day);
1774       d.setMonth(this.getMonth() + value);
1775       break;
1776     case Date.YEAR:
1777       d.setFullYear(this.getFullYear() + value);
1778       break;
1779   }
1780   return d;
1781 };
1782 /*
1783  * Based on:
1784  * Ext JS Library 1.1.1
1785  * Copyright(c) 2006-2007, Ext JS, LLC.
1786  *
1787  * Originally Released Under LGPL - original licence link has changed is not relivant.
1788  *
1789  * Fork - LGPL
1790  * <script type="text/javascript">
1791  */
1792
1793 /**
1794  * @class Roo.lib.Dom
1795  * @static
1796  * 
1797  * Dom utils (from YIU afaik)
1798  * 
1799  **/
1800 Roo.lib.Dom = {
1801     /**
1802      * Get the view width
1803      * @param {Boolean} full True will get the full document, otherwise it's the view width
1804      * @return {Number} The width
1805      */
1806      
1807     getViewWidth : function(full) {
1808         return full ? this.getDocumentWidth() : this.getViewportWidth();
1809     },
1810     /**
1811      * Get the view height
1812      * @param {Boolean} full True will get the full document, otherwise it's the view height
1813      * @return {Number} The height
1814      */
1815     getViewHeight : function(full) {
1816         return full ? this.getDocumentHeight() : this.getViewportHeight();
1817     },
1818
1819     getDocumentHeight: function() {
1820         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1821         return Math.max(scrollHeight, this.getViewportHeight());
1822     },
1823
1824     getDocumentWidth: function() {
1825         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1826         return Math.max(scrollWidth, this.getViewportWidth());
1827     },
1828
1829     getViewportHeight: function() {
1830         var height = self.innerHeight;
1831         var mode = document.compatMode;
1832
1833         if ((mode || Roo.isIE) && !Roo.isOpera) {
1834             height = (mode == "CSS1Compat") ?
1835                      document.documentElement.clientHeight :
1836                      document.body.clientHeight;
1837         }
1838
1839         return height;
1840     },
1841
1842     getViewportWidth: function() {
1843         var width = self.innerWidth;
1844         var mode = document.compatMode;
1845
1846         if (mode || Roo.isIE) {
1847             width = (mode == "CSS1Compat") ?
1848                     document.documentElement.clientWidth :
1849                     document.body.clientWidth;
1850         }
1851         return width;
1852     },
1853
1854     isAncestor : function(p, c) {
1855         p = Roo.getDom(p);
1856         c = Roo.getDom(c);
1857         if (!p || !c) {
1858             return false;
1859         }
1860
1861         if (p.contains && !Roo.isSafari) {
1862             return p.contains(c);
1863         } else if (p.compareDocumentPosition) {
1864             return !!(p.compareDocumentPosition(c) & 16);
1865         } else {
1866             var parent = c.parentNode;
1867             while (parent) {
1868                 if (parent == p) {
1869                     return true;
1870                 }
1871                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1872                     return false;
1873                 }
1874                 parent = parent.parentNode;
1875             }
1876             return false;
1877         }
1878     },
1879
1880     getRegion : function(el) {
1881         return Roo.lib.Region.getRegion(el);
1882     },
1883
1884     getY : function(el) {
1885         return this.getXY(el)[1];
1886     },
1887
1888     getX : function(el) {
1889         return this.getXY(el)[0];
1890     },
1891
1892     getXY : function(el) {
1893         var p, pe, b, scroll, bd = document.body;
1894         el = Roo.getDom(el);
1895         var fly = Roo.lib.AnimBase.fly;
1896         if (el.getBoundingClientRect) {
1897             b = el.getBoundingClientRect();
1898             scroll = fly(document).getScroll();
1899             return [b.left + scroll.left, b.top + scroll.top];
1900         }
1901         var x = 0, y = 0;
1902
1903         p = el;
1904
1905         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1906
1907         while (p) {
1908
1909             x += p.offsetLeft;
1910             y += p.offsetTop;
1911
1912             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1913                 hasAbsolute = true;
1914             }
1915
1916             if (Roo.isGecko) {
1917                 pe = fly(p);
1918
1919                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1920                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1921
1922
1923                 x += bl;
1924                 y += bt;
1925
1926
1927                 if (p != el && pe.getStyle('overflow') != 'visible') {
1928                     x += bl;
1929                     y += bt;
1930                 }
1931             }
1932             p = p.offsetParent;
1933         }
1934
1935         if (Roo.isSafari && hasAbsolute) {
1936             x -= bd.offsetLeft;
1937             y -= bd.offsetTop;
1938         }
1939
1940         if (Roo.isGecko && !hasAbsolute) {
1941             var dbd = fly(bd);
1942             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1943             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1944         }
1945
1946         p = el.parentNode;
1947         while (p && p != bd) {
1948             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1949                 x -= p.scrollLeft;
1950                 y -= p.scrollTop;
1951             }
1952             p = p.parentNode;
1953         }
1954         return [x, y];
1955     },
1956  
1957   
1958
1959
1960     setXY : function(el, xy) {
1961         el = Roo.fly(el, '_setXY');
1962         el.position();
1963         var pts = el.translatePoints(xy);
1964         if (xy[0] !== false) {
1965             el.dom.style.left = pts.left + "px";
1966         }
1967         if (xy[1] !== false) {
1968             el.dom.style.top = pts.top + "px";
1969         }
1970     },
1971
1972     setX : function(el, x) {
1973         this.setXY(el, [x, false]);
1974     },
1975
1976     setY : function(el, y) {
1977         this.setXY(el, [false, y]);
1978     }
1979 };
1980 /*
1981  * Portions of this file are based on pieces of Yahoo User Interface Library
1982  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1983  * YUI licensed under the BSD License:
1984  * http://developer.yahoo.net/yui/license.txt
1985  * <script type="text/javascript">
1986  *
1987  */
1988
1989 Roo.lib.Event = function() {
1990     var loadComplete = false;
1991     var listeners = [];
1992     var unloadListeners = [];
1993     var retryCount = 0;
1994     var onAvailStack = [];
1995     var counter = 0;
1996     var lastError = null;
1997
1998     return {
1999         POLL_RETRYS: 200,
2000         POLL_INTERVAL: 20,
2001         EL: 0,
2002         TYPE: 1,
2003         FN: 2,
2004         WFN: 3,
2005         OBJ: 3,
2006         ADJ_SCOPE: 4,
2007         _interval: null,
2008
2009         startInterval: function() {
2010             if (!this._interval) {
2011                 var self = this;
2012                 var callback = function() {
2013                     self._tryPreloadAttach();
2014                 };
2015                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2016
2017             }
2018         },
2019
2020         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2021             onAvailStack.push({ id:         p_id,
2022                 fn:         p_fn,
2023                 obj:        p_obj,
2024                 override:   p_override,
2025                 checkReady: false    });
2026
2027             retryCount = this.POLL_RETRYS;
2028             this.startInterval();
2029         },
2030
2031
2032         addListener: function(el, eventName, fn) {
2033             el = Roo.getDom(el);
2034             if (!el || !fn) {
2035                 return false;
2036             }
2037
2038             if ("unload" == eventName) {
2039                 unloadListeners[unloadListeners.length] =
2040                 [el, eventName, fn];
2041                 return true;
2042             }
2043
2044             var wrappedFn = function(e) {
2045                 return fn(Roo.lib.Event.getEvent(e));
2046             };
2047
2048             var li = [el, eventName, fn, wrappedFn];
2049
2050             var index = listeners.length;
2051             listeners[index] = li;
2052
2053             this.doAdd(el, eventName, wrappedFn, false);
2054             return true;
2055
2056         },
2057
2058
2059         removeListener: function(el, eventName, fn) {
2060             var i, len;
2061
2062             el = Roo.getDom(el);
2063
2064             if(!fn) {
2065                 return this.purgeElement(el, false, eventName);
2066             }
2067
2068
2069             if ("unload" == eventName) {
2070
2071                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2072                     var li = unloadListeners[i];
2073                     if (li &&
2074                         li[0] == el &&
2075                         li[1] == eventName &&
2076                         li[2] == fn) {
2077                         unloadListeners.splice(i, 1);
2078                         return true;
2079                     }
2080                 }
2081
2082                 return false;
2083             }
2084
2085             var cacheItem = null;
2086
2087
2088             var index = arguments[3];
2089
2090             if ("undefined" == typeof index) {
2091                 index = this._getCacheIndex(el, eventName, fn);
2092             }
2093
2094             if (index >= 0) {
2095                 cacheItem = listeners[index];
2096             }
2097
2098             if (!el || !cacheItem) {
2099                 return false;
2100             }
2101
2102             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2103
2104             delete listeners[index][this.WFN];
2105             delete listeners[index][this.FN];
2106             listeners.splice(index, 1);
2107
2108             return true;
2109
2110         },
2111
2112
2113         getTarget: function(ev, resolveTextNode) {
2114             ev = ev.browserEvent || ev;
2115             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2116             var t = ev.target || ev.srcElement;
2117             return this.resolveTextNode(t);
2118         },
2119
2120
2121         resolveTextNode: function(node) {
2122             if (Roo.isSafari && node && 3 == node.nodeType) {
2123                 return node.parentNode;
2124             } else {
2125                 return node;
2126             }
2127         },
2128
2129
2130         getPageX: function(ev) {
2131             ev = ev.browserEvent || ev;
2132             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2133             var x = ev.pageX;
2134             if (!x && 0 !== x) {
2135                 x = ev.clientX || 0;
2136
2137                 if (Roo.isIE) {
2138                     x += this.getScroll()[1];
2139                 }
2140             }
2141
2142             return x;
2143         },
2144
2145
2146         getPageY: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2149             var y = ev.pageY;
2150             if (!y && 0 !== y) {
2151                 y = ev.clientY || 0;
2152
2153                 if (Roo.isIE) {
2154                     y += this.getScroll()[0];
2155                 }
2156             }
2157
2158
2159             return y;
2160         },
2161
2162
2163         getXY: function(ev) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             return [this.getPageX(ev), this.getPageY(ev)];
2167         },
2168
2169
2170         getRelatedTarget: function(ev) {
2171             ev = ev.browserEvent || ev;
2172             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2173             var t = ev.relatedTarget;
2174             if (!t) {
2175                 if (ev.type == "mouseout") {
2176                     t = ev.toElement;
2177                 } else if (ev.type == "mouseover") {
2178                     t = ev.fromElement;
2179                 }
2180             }
2181
2182             return this.resolveTextNode(t);
2183         },
2184
2185
2186         getTime: function(ev) {
2187             ev = ev.browserEvent || ev;
2188             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2189             if (!ev.time) {
2190                 var t = new Date().getTime();
2191                 try {
2192                     ev.time = t;
2193                 } catch(ex) {
2194                     this.lastError = ex;
2195                     return t;
2196                 }
2197             }
2198
2199             return ev.time;
2200         },
2201
2202
2203         stopEvent: function(ev) {
2204             this.stopPropagation(ev);
2205             this.preventDefault(ev);
2206         },
2207
2208
2209         stopPropagation: function(ev) {
2210             ev = ev.browserEvent || ev;
2211             if (ev.stopPropagation) {
2212                 ev.stopPropagation();
2213             } else {
2214                 ev.cancelBubble = true;
2215             }
2216         },
2217
2218
2219         preventDefault: function(ev) {
2220             ev = ev.browserEvent || ev;
2221             if(ev.preventDefault) {
2222                 ev.preventDefault();
2223             } else {
2224                 ev.returnValue = false;
2225             }
2226         },
2227
2228
2229         getEvent: function(e) {
2230             var ev = e || window.event;
2231             if (!ev) {
2232                 var c = this.getEvent.caller;
2233                 while (c) {
2234                     ev = c.arguments[0];
2235                     if (ev && Event == ev.constructor) {
2236                         break;
2237                     }
2238                     c = c.caller;
2239                 }
2240             }
2241             return ev;
2242         },
2243
2244
2245         getCharCode: function(ev) {
2246             ev = ev.browserEvent || ev;
2247             return ev.charCode || ev.keyCode || 0;
2248         },
2249
2250
2251         _getCacheIndex: function(el, eventName, fn) {
2252             for (var i = 0,len = listeners.length; i < len; ++i) {
2253                 var li = listeners[i];
2254                 if (li &&
2255                     li[this.FN] == fn &&
2256                     li[this.EL] == el &&
2257                     li[this.TYPE] == eventName) {
2258                     return i;
2259                 }
2260             }
2261
2262             return -1;
2263         },
2264
2265
2266         elCache: {},
2267
2268
2269         getEl: function(id) {
2270             return document.getElementById(id);
2271         },
2272
2273
2274         clearCache: function() {
2275         },
2276
2277
2278         _load: function(e) {
2279             loadComplete = true;
2280             var EU = Roo.lib.Event;
2281
2282
2283             if (Roo.isIE) {
2284                 EU.doRemove(window, "load", EU._load);
2285             }
2286         },
2287
2288
2289         _tryPreloadAttach: function() {
2290
2291             if (this.locked) {
2292                 return false;
2293             }
2294
2295             this.locked = true;
2296
2297
2298             var tryAgain = !loadComplete;
2299             if (!tryAgain) {
2300                 tryAgain = (retryCount > 0);
2301             }
2302
2303
2304             var notAvail = [];
2305             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2306                 var item = onAvailStack[i];
2307                 if (item) {
2308                     var el = this.getEl(item.id);
2309
2310                     if (el) {
2311                         if (!item.checkReady ||
2312                             loadComplete ||
2313                             el.nextSibling ||
2314                             (document && document.body)) {
2315
2316                             var scope = el;
2317                             if (item.override) {
2318                                 if (item.override === true) {
2319                                     scope = item.obj;
2320                                 } else {
2321                                     scope = item.override;
2322                                 }
2323                             }
2324                             item.fn.call(scope, item.obj);
2325                             onAvailStack[i] = null;
2326                         }
2327                     } else {
2328                         notAvail.push(item);
2329                     }
2330                 }
2331             }
2332
2333             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2334
2335             if (tryAgain) {
2336
2337                 this.startInterval();
2338             } else {
2339                 clearInterval(this._interval);
2340                 this._interval = null;
2341             }
2342
2343             this.locked = false;
2344
2345             return true;
2346
2347         },
2348
2349
2350         purgeElement: function(el, recurse, eventName) {
2351             var elListeners = this.getListeners(el, eventName);
2352             if (elListeners) {
2353                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2354                     var l = elListeners[i];
2355                     this.removeListener(el, l.type, l.fn);
2356                 }
2357             }
2358
2359             if (recurse && el && el.childNodes) {
2360                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2361                     this.purgeElement(el.childNodes[i], recurse, eventName);
2362                 }
2363             }
2364         },
2365
2366
2367         getListeners: function(el, eventName) {
2368             var results = [], searchLists;
2369             if (!eventName) {
2370                 searchLists = [listeners, unloadListeners];
2371             } else if (eventName == "unload") {
2372                 searchLists = [unloadListeners];
2373             } else {
2374                 searchLists = [listeners];
2375             }
2376
2377             for (var j = 0; j < searchLists.length; ++j) {
2378                 var searchList = searchLists[j];
2379                 if (searchList && searchList.length > 0) {
2380                     for (var i = 0,len = searchList.length; i < len; ++i) {
2381                         var l = searchList[i];
2382                         if (l && l[this.EL] === el &&
2383                             (!eventName || eventName === l[this.TYPE])) {
2384                             results.push({
2385                                 type:   l[this.TYPE],
2386                                 fn:     l[this.FN],
2387                                 obj:    l[this.OBJ],
2388                                 adjust: l[this.ADJ_SCOPE],
2389                                 index:  i
2390                             });
2391                         }
2392                     }
2393                 }
2394             }
2395
2396             return (results.length) ? results : null;
2397         },
2398
2399
2400         _unload: function(e) {
2401
2402             var EU = Roo.lib.Event, i, j, l, len, index;
2403
2404             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2405                 l = unloadListeners[i];
2406                 if (l) {
2407                     var scope = window;
2408                     if (l[EU.ADJ_SCOPE]) {
2409                         if (l[EU.ADJ_SCOPE] === true) {
2410                             scope = l[EU.OBJ];
2411                         } else {
2412                             scope = l[EU.ADJ_SCOPE];
2413                         }
2414                     }
2415                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2416                     unloadListeners[i] = null;
2417                     l = null;
2418                     scope = null;
2419                 }
2420             }
2421
2422             unloadListeners = null;
2423
2424             if (listeners && listeners.length > 0) {
2425                 j = listeners.length;
2426                 while (j) {
2427                     index = j - 1;
2428                     l = listeners[index];
2429                     if (l) {
2430                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2431                                 l[EU.FN], index);
2432                     }
2433                     j = j - 1;
2434                 }
2435                 l = null;
2436
2437                 EU.clearCache();
2438             }
2439
2440             EU.doRemove(window, "unload", EU._unload);
2441
2442         },
2443
2444
2445         getScroll: function() {
2446             var dd = document.documentElement, db = document.body;
2447             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2448                 return [dd.scrollTop, dd.scrollLeft];
2449             } else if (db) {
2450                 return [db.scrollTop, db.scrollLeft];
2451             } else {
2452                 return [0, 0];
2453             }
2454         },
2455
2456
2457         doAdd: function () {
2458             if (window.addEventListener) {
2459                 return function(el, eventName, fn, capture) {
2460                     el.addEventListener(eventName, fn, (capture));
2461                 };
2462             } else if (window.attachEvent) {
2463                 return function(el, eventName, fn, capture) {
2464                     el.attachEvent("on" + eventName, fn);
2465                 };
2466             } else {
2467                 return function() {
2468                 };
2469             }
2470         }(),
2471
2472
2473         doRemove: function() {
2474             if (window.removeEventListener) {
2475                 return function (el, eventName, fn, capture) {
2476                     el.removeEventListener(eventName, fn, (capture));
2477                 };
2478             } else if (window.detachEvent) {
2479                 return function (el, eventName, fn) {
2480                     el.detachEvent("on" + eventName, fn);
2481                 };
2482             } else {
2483                 return function() {
2484                 };
2485             }
2486         }()
2487     };
2488     
2489 }();
2490 (function() {     
2491    
2492     var E = Roo.lib.Event;
2493     E.on = E.addListener;
2494     E.un = E.removeListener;
2495
2496     if (document && document.body) {
2497         E._load();
2498     } else {
2499         E.doAdd(window, "load", E._load);
2500     }
2501     E.doAdd(window, "unload", E._unload);
2502     E._tryPreloadAttach();
2503 })();
2504
2505 /*
2506  * Portions of this file are based on pieces of Yahoo User Interface Library
2507  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2508  * YUI licensed under the BSD License:
2509  * http://developer.yahoo.net/yui/license.txt
2510  * <script type="text/javascript">
2511  *
2512  */
2513
2514 (function() {
2515     /**
2516      * @class Roo.lib.Ajax
2517      *
2518      */
2519     Roo.lib.Ajax = {
2520         /**
2521          * @static 
2522          */
2523         request : function(method, uri, cb, data, options) {
2524             if(options){
2525                 var hs = options.headers;
2526                 if(hs){
2527                     for(var h in hs){
2528                         if(hs.hasOwnProperty(h)){
2529                             this.initHeader(h, hs[h], false);
2530                         }
2531                     }
2532                 }
2533                 if(options.xmlData){
2534                     this.initHeader('Content-Type', 'text/xml', false);
2535                     method = 'POST';
2536                     data = options.xmlData;
2537                 }
2538             }
2539
2540             return this.asyncRequest(method, uri, cb, data);
2541         },
2542
2543         serializeForm : function(form) {
2544             if(typeof form == 'string') {
2545                 form = (document.getElementById(form) || document.forms[form]);
2546             }
2547
2548             var el, name, val, disabled, data = '', hasSubmit = false;
2549             for (var i = 0; i < form.elements.length; i++) {
2550                 el = form.elements[i];
2551                 disabled = form.elements[i].disabled;
2552                 name = form.elements[i].name;
2553                 val = form.elements[i].value;
2554
2555                 if (!disabled && name){
2556                     switch (el.type)
2557                             {
2558                         case 'select-one':
2559                         case 'select-multiple':
2560                             for (var j = 0; j < el.options.length; j++) {
2561                                 if (el.options[j].selected) {
2562                                     if (Roo.isIE) {
2563                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2564                                     }
2565                                     else {
2566                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2567                                     }
2568                                 }
2569                             }
2570                             break;
2571                         case 'radio':
2572                         case 'checkbox':
2573                             if (el.checked) {
2574                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2575                             }
2576                             break;
2577                         case 'file':
2578
2579                         case undefined:
2580
2581                         case 'reset':
2582
2583                         case 'button':
2584
2585                             break;
2586                         case 'submit':
2587                             if(hasSubmit == false) {
2588                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2589                                 hasSubmit = true;
2590                             }
2591                             break;
2592                         default:
2593                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2594                             break;
2595                     }
2596                 }
2597             }
2598             data = data.substr(0, data.length - 1);
2599             return data;
2600         },
2601
2602         headers:{},
2603
2604         hasHeaders:false,
2605
2606         useDefaultHeader:true,
2607
2608         defaultPostHeader:'application/x-www-form-urlencoded',
2609
2610         useDefaultXhrHeader:true,
2611
2612         defaultXhrHeader:'XMLHttpRequest',
2613
2614         hasDefaultHeaders:true,
2615
2616         defaultHeaders:{},
2617
2618         poll:{},
2619
2620         timeout:{},
2621
2622         pollInterval:50,
2623
2624         transactionId:0,
2625
2626         setProgId:function(id)
2627         {
2628             this.activeX.unshift(id);
2629         },
2630
2631         setDefaultPostHeader:function(b)
2632         {
2633             this.useDefaultHeader = b;
2634         },
2635
2636         setDefaultXhrHeader:function(b)
2637         {
2638             this.useDefaultXhrHeader = b;
2639         },
2640
2641         setPollingInterval:function(i)
2642         {
2643             if (typeof i == 'number' && isFinite(i)) {
2644                 this.pollInterval = i;
2645             }
2646         },
2647
2648         createXhrObject:function(transactionId)
2649         {
2650             var obj,http;
2651             try
2652             {
2653
2654                 http = new XMLHttpRequest();
2655
2656                 obj = { conn:http, tId:transactionId };
2657             }
2658             catch(e)
2659             {
2660                 for (var i = 0; i < this.activeX.length; ++i) {
2661                     try
2662                     {
2663
2664                         http = new ActiveXObject(this.activeX[i]);
2665
2666                         obj = { conn:http, tId:transactionId };
2667                         break;
2668                     }
2669                     catch(e) {
2670                     }
2671                 }
2672             }
2673             finally
2674             {
2675                 return obj;
2676             }
2677         },
2678
2679         getConnectionObject:function()
2680         {
2681             var o;
2682             var tId = this.transactionId;
2683
2684             try
2685             {
2686                 o = this.createXhrObject(tId);
2687                 if (o) {
2688                     this.transactionId++;
2689                 }
2690             }
2691             catch(e) {
2692             }
2693             finally
2694             {
2695                 return o;
2696             }
2697         },
2698
2699         asyncRequest:function(method, uri, callback, postData)
2700         {
2701             var o = this.getConnectionObject();
2702
2703             if (!o) {
2704                 return null;
2705             }
2706             else {
2707                 o.conn.open(method, uri, true);
2708
2709                 if (this.useDefaultXhrHeader) {
2710                     if (!this.defaultHeaders['X-Requested-With']) {
2711                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2712                     }
2713                 }
2714
2715                 if(postData && this.useDefaultHeader){
2716                     this.initHeader('Content-Type', this.defaultPostHeader);
2717                 }
2718
2719                  if (this.hasDefaultHeaders || this.hasHeaders) {
2720                     this.setHeader(o);
2721                 }
2722
2723                 this.handleReadyState(o, callback);
2724                 o.conn.send(postData || null);
2725
2726                 return o;
2727             }
2728         },
2729
2730         handleReadyState:function(o, callback)
2731         {
2732             var oConn = this;
2733
2734             if (callback && callback.timeout) {
2735                 
2736                 this.timeout[o.tId] = window.setTimeout(function() {
2737                     oConn.abort(o, callback, true);
2738                 }, callback.timeout);
2739             }
2740
2741             this.poll[o.tId] = window.setInterval(
2742                     function() {
2743                         if (o.conn && o.conn.readyState == 4) {
2744                             window.clearInterval(oConn.poll[o.tId]);
2745                             delete oConn.poll[o.tId];
2746
2747                             if(callback && callback.timeout) {
2748                                 window.clearTimeout(oConn.timeout[o.tId]);
2749                                 delete oConn.timeout[o.tId];
2750                             }
2751
2752                             oConn.handleTransactionResponse(o, callback);
2753                         }
2754                     }
2755                     , this.pollInterval);
2756         },
2757
2758         handleTransactionResponse:function(o, callback, isAbort)
2759         {
2760
2761             if (!callback) {
2762                 this.releaseObject(o);
2763                 return;
2764             }
2765
2766             var httpStatus, responseObject;
2767
2768             try
2769             {
2770                 if (o.conn.status !== undefined && o.conn.status != 0) {
2771                     httpStatus = o.conn.status;
2772                 }
2773                 else {
2774                     httpStatus = 13030;
2775                 }
2776             }
2777             catch(e) {
2778
2779
2780                 httpStatus = 13030;
2781             }
2782
2783             if (httpStatus >= 200 && httpStatus < 300) {
2784                 responseObject = this.createResponseObject(o, callback.argument);
2785                 if (callback.success) {
2786                     if (!callback.scope) {
2787                         callback.success(responseObject);
2788                     }
2789                     else {
2790
2791
2792                         callback.success.apply(callback.scope, [responseObject]);
2793                     }
2794                 }
2795             }
2796             else {
2797                 switch (httpStatus) {
2798
2799                     case 12002:
2800                     case 12029:
2801                     case 12030:
2802                     case 12031:
2803                     case 12152:
2804                     case 13030:
2805                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                         break;
2815                     default:
2816                         responseObject = this.createResponseObject(o, callback.argument);
2817                         if (callback.failure) {
2818                             if (!callback.scope) {
2819                                 callback.failure(responseObject);
2820                             }
2821                             else {
2822                                 callback.failure.apply(callback.scope, [responseObject]);
2823                             }
2824                         }
2825                 }
2826             }
2827
2828             this.releaseObject(o);
2829             responseObject = null;
2830         },
2831
2832         createResponseObject:function(o, callbackArg)
2833         {
2834             var obj = {};
2835             var headerObj = {};
2836
2837             try
2838             {
2839                 var headerStr = o.conn.getAllResponseHeaders();
2840                 var header = headerStr.split('\n');
2841                 for (var i = 0; i < header.length; i++) {
2842                     var delimitPos = header[i].indexOf(':');
2843                     if (delimitPos != -1) {
2844                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2845                     }
2846                 }
2847             }
2848             catch(e) {
2849             }
2850
2851             obj.tId = o.tId;
2852             obj.status = o.conn.status;
2853             obj.statusText = o.conn.statusText;
2854             obj.getResponseHeader = headerObj;
2855             obj.getAllResponseHeaders = headerStr;
2856             obj.responseText = o.conn.responseText;
2857             obj.responseXML = o.conn.responseXML;
2858
2859             if (typeof callbackArg !== undefined) {
2860                 obj.argument = callbackArg;
2861             }
2862
2863             return obj;
2864         },
2865
2866         createExceptionObject:function(tId, callbackArg, isAbort)
2867         {
2868             var COMM_CODE = 0;
2869             var COMM_ERROR = 'communication failure';
2870             var ABORT_CODE = -1;
2871             var ABORT_ERROR = 'transaction aborted';
2872
2873             var obj = {};
2874
2875             obj.tId = tId;
2876             if (isAbort) {
2877                 obj.status = ABORT_CODE;
2878                 obj.statusText = ABORT_ERROR;
2879             }
2880             else {
2881                 obj.status = COMM_CODE;
2882                 obj.statusText = COMM_ERROR;
2883             }
2884
2885             if (callbackArg) {
2886                 obj.argument = callbackArg;
2887             }
2888
2889             return obj;
2890         },
2891
2892         initHeader:function(label, value, isDefault)
2893         {
2894             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2895
2896             if (headerObj[label] === undefined) {
2897                 headerObj[label] = value;
2898             }
2899             else {
2900
2901
2902                 headerObj[label] = value + "," + headerObj[label];
2903             }
2904
2905             if (isDefault) {
2906                 this.hasDefaultHeaders = true;
2907             }
2908             else {
2909                 this.hasHeaders = true;
2910             }
2911         },
2912
2913
2914         setHeader:function(o)
2915         {
2916             if (this.hasDefaultHeaders) {
2917                 for (var prop in this.defaultHeaders) {
2918                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2919                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2920                     }
2921                 }
2922             }
2923
2924             if (this.hasHeaders) {
2925                 for (var prop in this.headers) {
2926                     if (this.headers.hasOwnProperty(prop)) {
2927                         o.conn.setRequestHeader(prop, this.headers[prop]);
2928                     }
2929                 }
2930                 this.headers = {};
2931                 this.hasHeaders = false;
2932             }
2933         },
2934
2935         resetDefaultHeaders:function() {
2936             delete this.defaultHeaders;
2937             this.defaultHeaders = {};
2938             this.hasDefaultHeaders = false;
2939         },
2940
2941         abort:function(o, callback, isTimeout)
2942         {
2943             if(this.isCallInProgress(o)) {
2944                 o.conn.abort();
2945                 window.clearInterval(this.poll[o.tId]);
2946                 delete this.poll[o.tId];
2947                 if (isTimeout) {
2948                     delete this.timeout[o.tId];
2949                 }
2950
2951                 this.handleTransactionResponse(o, callback, true);
2952
2953                 return true;
2954             }
2955             else {
2956                 return false;
2957             }
2958         },
2959
2960
2961         isCallInProgress:function(o)
2962         {
2963             if (o && o.conn) {
2964                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2965             }
2966             else {
2967
2968                 return false;
2969             }
2970         },
2971
2972
2973         releaseObject:function(o)
2974         {
2975
2976             o.conn = null;
2977
2978             o = null;
2979         },
2980
2981         activeX:[
2982         'MSXML2.XMLHTTP.3.0',
2983         'MSXML2.XMLHTTP',
2984         'Microsoft.XMLHTTP'
2985         ]
2986
2987
2988     };
2989 })();/*
2990  * Portions of this file are based on pieces of Yahoo User Interface Library
2991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2992  * YUI licensed under the BSD License:
2993  * http://developer.yahoo.net/yui/license.txt
2994  * <script type="text/javascript">
2995  *
2996  */
2997
2998 Roo.lib.Region = function(t, r, b, l) {
2999     this.top = t;
3000     this[1] = t;
3001     this.right = r;
3002     this.bottom = b;
3003     this.left = l;
3004     this[0] = l;
3005 };
3006
3007
3008 Roo.lib.Region.prototype = {
3009     contains : function(region) {
3010         return ( region.left >= this.left &&
3011                  region.right <= this.right &&
3012                  region.top >= this.top &&
3013                  region.bottom <= this.bottom    );
3014
3015     },
3016
3017     getArea : function() {
3018         return ( (this.bottom - this.top) * (this.right - this.left) );
3019     },
3020
3021     intersect : function(region) {
3022         var t = Math.max(this.top, region.top);
3023         var r = Math.min(this.right, region.right);
3024         var b = Math.min(this.bottom, region.bottom);
3025         var l = Math.max(this.left, region.left);
3026
3027         if (b >= t && r >= l) {
3028             return new Roo.lib.Region(t, r, b, l);
3029         } else {
3030             return null;
3031         }
3032     },
3033     union : function(region) {
3034         var t = Math.min(this.top, region.top);
3035         var r = Math.max(this.right, region.right);
3036         var b = Math.max(this.bottom, region.bottom);
3037         var l = Math.min(this.left, region.left);
3038
3039         return new Roo.lib.Region(t, r, b, l);
3040     },
3041
3042     adjust : function(t, l, b, r) {
3043         this.top += t;
3044         this.left += l;
3045         this.right += r;
3046         this.bottom += b;
3047         return this;
3048     }
3049 };
3050
3051 Roo.lib.Region.getRegion = function(el) {
3052     var p = Roo.lib.Dom.getXY(el);
3053
3054     var t = p[1];
3055     var r = p[0] + el.offsetWidth;
3056     var b = p[1] + el.offsetHeight;
3057     var l = p[0];
3058
3059     return new Roo.lib.Region(t, r, b, l);
3060 };
3061 /*
3062  * Portions of this file are based on pieces of Yahoo User Interface Library
3063  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3064  * YUI licensed under the BSD License:
3065  * http://developer.yahoo.net/yui/license.txt
3066  * <script type="text/javascript">
3067  *
3068  */
3069 //@@dep Roo.lib.Region
3070
3071
3072 Roo.lib.Point = function(x, y) {
3073     if (x instanceof Array) {
3074         y = x[1];
3075         x = x[0];
3076     }
3077     this.x = this.right = this.left = this[0] = x;
3078     this.y = this.top = this.bottom = this[1] = y;
3079 };
3080
3081 Roo.lib.Point.prototype = new Roo.lib.Region();
3082 /*
3083  * Portions of this file are based on pieces of Yahoo User Interface Library
3084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3085  * YUI licensed under the BSD License:
3086  * http://developer.yahoo.net/yui/license.txt
3087  * <script type="text/javascript">
3088  *
3089  */
3090  
3091 (function() {   
3092
3093     Roo.lib.Anim = {
3094         scroll : function(el, args, duration, easing, cb, scope) {
3095             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3096         },
3097
3098         motion : function(el, args, duration, easing, cb, scope) {
3099             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3100         },
3101
3102         color : function(el, args, duration, easing, cb, scope) {
3103             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3104         },
3105
3106         run : function(el, args, duration, easing, cb, scope, type) {
3107             type = type || Roo.lib.AnimBase;
3108             if (typeof easing == "string") {
3109                 easing = Roo.lib.Easing[easing];
3110             }
3111             var anim = new type(el, args, duration, easing);
3112             anim.animateX(function() {
3113                 Roo.callback(cb, scope);
3114             });
3115             return anim;
3116         }
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 (function() {    
3128     var libFlyweight;
3129     
3130     function fly(el) {
3131         if (!libFlyweight) {
3132             libFlyweight = new Roo.Element.Flyweight();
3133         }
3134         libFlyweight.dom = el;
3135         return libFlyweight;
3136     }
3137
3138     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3139     
3140    
3141     
3142     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3143         if (el) {
3144             this.init(el, attributes, duration, method);
3145         }
3146     };
3147
3148     Roo.lib.AnimBase.fly = fly;
3149     
3150     
3151     
3152     Roo.lib.AnimBase.prototype = {
3153
3154         toString: function() {
3155             var el = this.getEl();
3156             var id = el.id || el.tagName;
3157             return ("Anim " + id);
3158         },
3159
3160         patterns: {
3161             noNegatives:        /width|height|opacity|padding/i,
3162             offsetAttribute:  /^((width|height)|(top|left))$/,
3163             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3164             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3165         },
3166
3167
3168         doMethod: function(attr, start, end) {
3169             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3170         },
3171
3172
3173         setAttribute: function(attr, val, unit) {
3174             if (this.patterns.noNegatives.test(attr)) {
3175                 val = (val > 0) ? val : 0;
3176             }
3177
3178             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3179         },
3180
3181
3182         getAttribute: function(attr) {
3183             var el = this.getEl();
3184             var val = fly(el).getStyle(attr);
3185
3186             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3187                 return parseFloat(val);
3188             }
3189
3190             var a = this.patterns.offsetAttribute.exec(attr) || [];
3191             var pos = !!( a[3] );
3192             var box = !!( a[2] );
3193
3194
3195             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3196                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3197             } else {
3198                 val = 0;
3199             }
3200
3201             return val;
3202         },
3203
3204
3205         getDefaultUnit: function(attr) {
3206             if (this.patterns.defaultUnit.test(attr)) {
3207                 return 'px';
3208             }
3209
3210             return '';
3211         },
3212
3213         animateX : function(callback, scope) {
3214             var f = function() {
3215                 this.onComplete.removeListener(f);
3216                 if (typeof callback == "function") {
3217                     callback.call(scope || this, this);
3218                 }
3219             };
3220             this.onComplete.addListener(f, this);
3221             this.animate();
3222         },
3223
3224
3225         setRuntimeAttribute: function(attr) {
3226             var start;
3227             var end;
3228             var attributes = this.attributes;
3229
3230             this.runtimeAttributes[attr] = {};
3231
3232             var isset = function(prop) {
3233                 return (typeof prop !== 'undefined');
3234             };
3235
3236             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3237                 return false;
3238             }
3239
3240             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3241
3242
3243             if (isset(attributes[attr]['to'])) {
3244                 end = attributes[attr]['to'];
3245             } else if (isset(attributes[attr]['by'])) {
3246                 if (start.constructor == Array) {
3247                     end = [];
3248                     for (var i = 0, len = start.length; i < len; ++i) {
3249                         end[i] = start[i] + attributes[attr]['by'][i];
3250                     }
3251                 } else {
3252                     end = start + attributes[attr]['by'];
3253                 }
3254             }
3255
3256             this.runtimeAttributes[attr].start = start;
3257             this.runtimeAttributes[attr].end = end;
3258
3259
3260             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3261         },
3262
3263
3264         init: function(el, attributes, duration, method) {
3265
3266             var isAnimated = false;
3267
3268
3269             var startTime = null;
3270
3271
3272             var actualFrames = 0;
3273
3274
3275             el = Roo.getDom(el);
3276
3277
3278             this.attributes = attributes || {};
3279
3280
3281             this.duration = duration || 1;
3282
3283
3284             this.method = method || Roo.lib.Easing.easeNone;
3285
3286
3287             this.useSeconds = true;
3288
3289
3290             this.currentFrame = 0;
3291
3292
3293             this.totalFrames = Roo.lib.AnimMgr.fps;
3294
3295
3296             this.getEl = function() {
3297                 return el;
3298             };
3299
3300
3301             this.isAnimated = function() {
3302                 return isAnimated;
3303             };
3304
3305
3306             this.getStartTime = function() {
3307                 return startTime;
3308             };
3309
3310             this.runtimeAttributes = {};
3311
3312
3313             this.animate = function() {
3314                 if (this.isAnimated()) {
3315                     return false;
3316                 }
3317
3318                 this.currentFrame = 0;
3319
3320                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3321
3322                 Roo.lib.AnimMgr.registerElement(this);
3323             };
3324
3325
3326             this.stop = function(finish) {
3327                 if (finish) {
3328                     this.currentFrame = this.totalFrames;
3329                     this._onTween.fire();
3330                 }
3331                 Roo.lib.AnimMgr.stop(this);
3332             };
3333
3334             var onStart = function() {
3335                 this.onStart.fire();
3336
3337                 this.runtimeAttributes = {};
3338                 for (var attr in this.attributes) {
3339                     this.setRuntimeAttribute(attr);
3340                 }
3341
3342                 isAnimated = true;
3343                 actualFrames = 0;
3344                 startTime = new Date();
3345             };
3346
3347
3348             var onTween = function() {
3349                 var data = {
3350                     duration: new Date() - this.getStartTime(),
3351                     currentFrame: this.currentFrame
3352                 };
3353
3354                 data.toString = function() {
3355                     return (
3356                             'duration: ' + data.duration +
3357                             ', currentFrame: ' + data.currentFrame
3358                             );
3359                 };
3360
3361                 this.onTween.fire(data);
3362
3363                 var runtimeAttributes = this.runtimeAttributes;
3364
3365                 for (var attr in runtimeAttributes) {
3366                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3367                 }
3368
3369                 actualFrames += 1;
3370             };
3371
3372             var onComplete = function() {
3373                 var actual_duration = (new Date() - startTime) / 1000 ;
3374
3375                 var data = {
3376                     duration: actual_duration,
3377                     frames: actualFrames,
3378                     fps: actualFrames / actual_duration
3379                 };
3380
3381                 data.toString = function() {
3382                     return (
3383                             'duration: ' + data.duration +
3384                             ', frames: ' + data.frames +
3385                             ', fps: ' + data.fps
3386                             );
3387                 };
3388
3389                 isAnimated = false;
3390                 actualFrames = 0;
3391                 this.onComplete.fire(data);
3392             };
3393
3394
3395             this._onStart = new Roo.util.Event(this);
3396             this.onStart = new Roo.util.Event(this);
3397             this.onTween = new Roo.util.Event(this);
3398             this._onTween = new Roo.util.Event(this);
3399             this.onComplete = new Roo.util.Event(this);
3400             this._onComplete = new Roo.util.Event(this);
3401             this._onStart.addListener(onStart);
3402             this._onTween.addListener(onTween);
3403             this._onComplete.addListener(onComplete);
3404         }
3405     };
3406 })();
3407 /*
3408  * Portions of this file are based on pieces of Yahoo User Interface Library
3409  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3410  * YUI licensed under the BSD License:
3411  * http://developer.yahoo.net/yui/license.txt
3412  * <script type="text/javascript">
3413  *
3414  */
3415
3416 Roo.lib.AnimMgr = new function() {
3417
3418     var thread = null;
3419
3420
3421     var queue = [];
3422
3423
3424     var tweenCount = 0;
3425
3426
3427     this.fps = 1000;
3428
3429
3430     this.delay = 1;
3431
3432
3433     this.registerElement = function(tween) {
3434         queue[queue.length] = tween;
3435         tweenCount += 1;
3436         tween._onStart.fire();
3437         this.start();
3438     };
3439
3440
3441     this.unRegister = function(tween, index) {
3442         tween._onComplete.fire();
3443         index = index || getIndex(tween);
3444         if (index != -1) {
3445             queue.splice(index, 1);
3446         }
3447
3448         tweenCount -= 1;
3449         if (tweenCount <= 0) {
3450             this.stop();
3451         }
3452     };
3453
3454
3455     this.start = function() {
3456         if (thread === null) {
3457             thread = setInterval(this.run, this.delay);
3458         }
3459     };
3460
3461
3462     this.stop = function(tween) {
3463         if (!tween) {
3464             clearInterval(thread);
3465
3466             for (var i = 0, len = queue.length; i < len; ++i) {
3467                 if (queue[0].isAnimated()) {
3468                     this.unRegister(queue[0], 0);
3469                 }
3470             }
3471
3472             queue = [];
3473             thread = null;
3474             tweenCount = 0;
3475         }
3476         else {
3477             this.unRegister(tween);
3478         }
3479     };
3480
3481
3482     this.run = function() {
3483         for (var i = 0, len = queue.length; i < len; ++i) {
3484             var tween = queue[i];
3485             if (!tween || !tween.isAnimated()) {
3486                 continue;
3487             }
3488
3489             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3490             {
3491                 tween.currentFrame += 1;
3492
3493                 if (tween.useSeconds) {
3494                     correctFrame(tween);
3495                 }
3496                 tween._onTween.fire();
3497             }
3498             else {
3499                 Roo.lib.AnimMgr.stop(tween, i);
3500             }
3501         }
3502     };
3503
3504     var getIndex = function(anim) {
3505         for (var i = 0, len = queue.length; i < len; ++i) {
3506             if (queue[i] == anim) {
3507                 return i;
3508             }
3509         }
3510         return -1;
3511     };
3512
3513
3514     var correctFrame = function(tween) {
3515         var frames = tween.totalFrames;
3516         var frame = tween.currentFrame;
3517         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3518         var elapsed = (new Date() - tween.getStartTime());
3519         var tweak = 0;
3520
3521         if (elapsed < tween.duration * 1000) {
3522             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3523         } else {
3524             tweak = frames - (frame + 1);
3525         }
3526         if (tweak > 0 && isFinite(tweak)) {
3527             if (tween.currentFrame + tweak >= frames) {
3528                 tweak = frames - (frame + 1);
3529             }
3530
3531             tween.currentFrame += tweak;
3532         }
3533     };
3534 };
3535
3536     /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544 Roo.lib.Bezier = new function() {
3545
3546         this.getPosition = function(points, t) {
3547             var n = points.length;
3548             var tmp = [];
3549
3550             for (var i = 0; i < n; ++i) {
3551                 tmp[i] = [points[i][0], points[i][1]];
3552             }
3553
3554             for (var j = 1; j < n; ++j) {
3555                 for (i = 0; i < n - j; ++i) {
3556                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3557                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3558                 }
3559             }
3560
3561             return [ tmp[0][0], tmp[0][1] ];
3562
3563         };
3564     };/*
3565  * Portions of this file are based on pieces of Yahoo User Interface Library
3566  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3567  * YUI licensed under the BSD License:
3568  * http://developer.yahoo.net/yui/license.txt
3569  * <script type="text/javascript">
3570  *
3571  */
3572 (function() {
3573
3574     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3575         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3576     };
3577
3578     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3579
3580     var fly = Roo.lib.AnimBase.fly;
3581     var Y = Roo.lib;
3582     var superclass = Y.ColorAnim.superclass;
3583     var proto = Y.ColorAnim.prototype;
3584
3585     proto.toString = function() {
3586         var el = this.getEl();
3587         var id = el.id || el.tagName;
3588         return ("ColorAnim " + id);
3589     };
3590
3591     proto.patterns.color = /color$/i;
3592     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3593     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3594     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3595     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3596
3597
3598     proto.parseColor = function(s) {
3599         if (s.length == 3) {
3600             return s;
3601         }
3602
3603         var c = this.patterns.hex.exec(s);
3604         if (c && c.length == 4) {
3605             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3606         }
3607
3608         c = this.patterns.rgb.exec(s);
3609         if (c && c.length == 4) {
3610             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3611         }
3612
3613         c = this.patterns.hex3.exec(s);
3614         if (c && c.length == 4) {
3615             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3616         }
3617
3618         return null;
3619     };
3620     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3621     proto.getAttribute = function(attr) {
3622         var el = this.getEl();
3623         if (this.patterns.color.test(attr)) {
3624             var val = fly(el).getStyle(attr);
3625
3626             if (this.patterns.transparent.test(val)) {
3627                 var parent = el.parentNode;
3628                 val = fly(parent).getStyle(attr);
3629
3630                 while (parent && this.patterns.transparent.test(val)) {
3631                     parent = parent.parentNode;
3632                     val = fly(parent).getStyle(attr);
3633                     if (parent.tagName.toUpperCase() == 'HTML') {
3634                         val = '#fff';
3635                     }
3636                 }
3637             }
3638         } else {
3639             val = superclass.getAttribute.call(this, attr);
3640         }
3641
3642         return val;
3643     };
3644     proto.getAttribute = function(attr) {
3645         var el = this.getEl();
3646         if (this.patterns.color.test(attr)) {
3647             var val = fly(el).getStyle(attr);
3648
3649             if (this.patterns.transparent.test(val)) {
3650                 var parent = el.parentNode;
3651                 val = fly(parent).getStyle(attr);
3652
3653                 while (parent && this.patterns.transparent.test(val)) {
3654                     parent = parent.parentNode;
3655                     val = fly(parent).getStyle(attr);
3656                     if (parent.tagName.toUpperCase() == 'HTML') {
3657                         val = '#fff';
3658                     }
3659                 }
3660             }
3661         } else {
3662             val = superclass.getAttribute.call(this, attr);
3663         }
3664
3665         return val;
3666     };
3667
3668     proto.doMethod = function(attr, start, end) {
3669         var val;
3670
3671         if (this.patterns.color.test(attr)) {
3672             val = [];
3673             for (var i = 0, len = start.length; i < len; ++i) {
3674                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3675             }
3676
3677             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3678         }
3679         else {
3680             val = superclass.doMethod.call(this, attr, start, end);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.setRuntimeAttribute = function(attr) {
3687         superclass.setRuntimeAttribute.call(this, attr);
3688
3689         if (this.patterns.color.test(attr)) {
3690             var attributes = this.attributes;
3691             var start = this.parseColor(this.runtimeAttributes[attr].start);
3692             var end = this.parseColor(this.runtimeAttributes[attr].end);
3693
3694             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3695                 end = this.parseColor(attributes[attr].by);
3696
3697                 for (var i = 0, len = start.length; i < len; ++i) {
3698                     end[i] = start[i] + end[i];
3699                 }
3700             }
3701
3702             this.runtimeAttributes[attr].start = start;
3703             this.runtimeAttributes[attr].end = end;
3704         }
3705     };
3706 })();
3707
3708 /*
3709  * Portions of this file are based on pieces of Yahoo User Interface Library
3710  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3711  * YUI licensed under the BSD License:
3712  * http://developer.yahoo.net/yui/license.txt
3713  * <script type="text/javascript">
3714  *
3715  */
3716 Roo.lib.Easing = {
3717
3718
3719     easeNone: function (t, b, c, d) {
3720         return c * t / d + b;
3721     },
3722
3723
3724     easeIn: function (t, b, c, d) {
3725         return c * (t /= d) * t + b;
3726     },
3727
3728
3729     easeOut: function (t, b, c, d) {
3730         return -c * (t /= d) * (t - 2) + b;
3731     },
3732
3733
3734     easeBoth: function (t, b, c, d) {
3735         if ((t /= d / 2) < 1) {
3736             return c / 2 * t * t + b;
3737         }
3738
3739         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3740     },
3741
3742
3743     easeInStrong: function (t, b, c, d) {
3744         return c * (t /= d) * t * t * t + b;
3745     },
3746
3747
3748     easeOutStrong: function (t, b, c, d) {
3749         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3750     },
3751
3752
3753     easeBothStrong: function (t, b, c, d) {
3754         if ((t /= d / 2) < 1) {
3755             return c / 2 * t * t * t * t + b;
3756         }
3757
3758         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3759     },
3760
3761
3762
3763     elasticIn: function (t, b, c, d, a, p) {
3764         if (t == 0) {
3765             return b;
3766         }
3767         if ((t /= d) == 1) {
3768             return b + c;
3769         }
3770         if (!p) {
3771             p = d * .3;
3772         }
3773
3774         if (!a || a < Math.abs(c)) {
3775             a = c;
3776             var s = p / 4;
3777         }
3778         else {
3779             var s = p / (2 * Math.PI) * Math.asin(c / a);
3780         }
3781
3782         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3783     },
3784
3785
3786     elasticOut: function (t, b, c, d, a, p) {
3787         if (t == 0) {
3788             return b;
3789         }
3790         if ((t /= d) == 1) {
3791             return b + c;
3792         }
3793         if (!p) {
3794             p = d * .3;
3795         }
3796
3797         if (!a || a < Math.abs(c)) {
3798             a = c;
3799             var s = p / 4;
3800         }
3801         else {
3802             var s = p / (2 * Math.PI) * Math.asin(c / a);
3803         }
3804
3805         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3806     },
3807
3808
3809     elasticBoth: function (t, b, c, d, a, p) {
3810         if (t == 0) {
3811             return b;
3812         }
3813
3814         if ((t /= d / 2) == 2) {
3815             return b + c;
3816         }
3817
3818         if (!p) {
3819             p = d * (.3 * 1.5);
3820         }
3821
3822         if (!a || a < Math.abs(c)) {
3823             a = c;
3824             var s = p / 4;
3825         }
3826         else {
3827             var s = p / (2 * Math.PI) * Math.asin(c / a);
3828         }
3829
3830         if (t < 1) {
3831             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3832                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833         }
3834         return a * Math.pow(2, -10 * (t -= 1)) *
3835                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3836     },
3837
3838
3839
3840     backIn: function (t, b, c, d, s) {
3841         if (typeof s == 'undefined') {
3842             s = 1.70158;
3843         }
3844         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3845     },
3846
3847
3848     backOut: function (t, b, c, d, s) {
3849         if (typeof s == 'undefined') {
3850             s = 1.70158;
3851         }
3852         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3853     },
3854
3855
3856     backBoth: function (t, b, c, d, s) {
3857         if (typeof s == 'undefined') {
3858             s = 1.70158;
3859         }
3860
3861         if ((t /= d / 2 ) < 1) {
3862             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3863         }
3864         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3865     },
3866
3867
3868     bounceIn: function (t, b, c, d) {
3869         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3870     },
3871
3872
3873     bounceOut: function (t, b, c, d) {
3874         if ((t /= d) < (1 / 2.75)) {
3875             return c * (7.5625 * t * t) + b;
3876         } else if (t < (2 / 2.75)) {
3877             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3878         } else if (t < (2.5 / 2.75)) {
3879             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3880         }
3881         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3882     },
3883
3884
3885     bounceBoth: function (t, b, c, d) {
3886         if (t < d / 2) {
3887             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3888         }
3889         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3890     }
3891 };/*
3892  * Portions of this file are based on pieces of Yahoo User Interface Library
3893  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3894  * YUI licensed under the BSD License:
3895  * http://developer.yahoo.net/yui/license.txt
3896  * <script type="text/javascript">
3897  *
3898  */
3899     (function() {
3900         Roo.lib.Motion = function(el, attributes, duration, method) {
3901             if (el) {
3902                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3903             }
3904         };
3905
3906         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3907
3908
3909         var Y = Roo.lib;
3910         var superclass = Y.Motion.superclass;
3911         var proto = Y.Motion.prototype;
3912
3913         proto.toString = function() {
3914             var el = this.getEl();
3915             var id = el.id || el.tagName;
3916             return ("Motion " + id);
3917         };
3918
3919         proto.patterns.points = /^points$/i;
3920
3921         proto.setAttribute = function(attr, val, unit) {
3922             if (this.patterns.points.test(attr)) {
3923                 unit = unit || 'px';
3924                 superclass.setAttribute.call(this, 'left', val[0], unit);
3925                 superclass.setAttribute.call(this, 'top', val[1], unit);
3926             } else {
3927                 superclass.setAttribute.call(this, attr, val, unit);
3928             }
3929         };
3930
3931         proto.getAttribute = function(attr) {
3932             if (this.patterns.points.test(attr)) {
3933                 var val = [
3934                         superclass.getAttribute.call(this, 'left'),
3935                         superclass.getAttribute.call(this, 'top')
3936                         ];
3937             } else {
3938                 val = superclass.getAttribute.call(this, attr);
3939             }
3940
3941             return val;
3942         };
3943
3944         proto.doMethod = function(attr, start, end) {
3945             var val = null;
3946
3947             if (this.patterns.points.test(attr)) {
3948                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3949                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3950             } else {
3951                 val = superclass.doMethod.call(this, attr, start, end);
3952             }
3953             return val;
3954         };
3955
3956         proto.setRuntimeAttribute = function(attr) {
3957             if (this.patterns.points.test(attr)) {
3958                 var el = this.getEl();
3959                 var attributes = this.attributes;
3960                 var start;
3961                 var control = attributes['points']['control'] || [];
3962                 var end;
3963                 var i, len;
3964
3965                 if (control.length > 0 && !(control[0] instanceof Array)) {
3966                     control = [control];
3967                 } else {
3968                     var tmp = [];
3969                     for (i = 0,len = control.length; i < len; ++i) {
3970                         tmp[i] = control[i];
3971                     }
3972                     control = tmp;
3973                 }
3974
3975                 Roo.fly(el).position();
3976
3977                 if (isset(attributes['points']['from'])) {
3978                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3979                 }
3980                 else {
3981                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3982                 }
3983
3984                 start = this.getAttribute('points');
3985
3986
3987                 if (isset(attributes['points']['to'])) {
3988                     end = translateValues.call(this, attributes['points']['to'], start);
3989
3990                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3991                     for (i = 0,len = control.length; i < len; ++i) {
3992                         control[i] = translateValues.call(this, control[i], start);
3993                     }
3994
3995
3996                 } else if (isset(attributes['points']['by'])) {
3997                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3998
3999                     for (i = 0,len = control.length; i < len; ++i) {
4000                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4001                     }
4002                 }
4003
4004                 this.runtimeAttributes[attr] = [start];
4005
4006                 if (control.length > 0) {
4007                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4008                 }
4009
4010                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4011             }
4012             else {
4013                 superclass.setRuntimeAttribute.call(this, attr);
4014             }
4015         };
4016
4017         var translateValues = function(val, start) {
4018             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4019             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4020
4021             return val;
4022         };
4023
4024         var isset = function(prop) {
4025             return (typeof prop !== 'undefined');
4026         };
4027     })();
4028 /*
4029  * Portions of this file are based on pieces of Yahoo User Interface Library
4030  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4031  * YUI licensed under the BSD License:
4032  * http://developer.yahoo.net/yui/license.txt
4033  * <script type="text/javascript">
4034  *
4035  */
4036     (function() {
4037         Roo.lib.Scroll = function(el, attributes, duration, method) {
4038             if (el) {
4039                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4040             }
4041         };
4042
4043         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4044
4045
4046         var Y = Roo.lib;
4047         var superclass = Y.Scroll.superclass;
4048         var proto = Y.Scroll.prototype;
4049
4050         proto.toString = function() {
4051             var el = this.getEl();
4052             var id = el.id || el.tagName;
4053             return ("Scroll " + id);
4054         };
4055
4056         proto.doMethod = function(attr, start, end) {
4057             var val = null;
4058
4059             if (attr == 'scroll') {
4060                 val = [
4061                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4062                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4063                         ];
4064
4065             } else {
4066                 val = superclass.doMethod.call(this, attr, start, end);
4067             }
4068             return val;
4069         };
4070
4071         proto.getAttribute = function(attr) {
4072             var val = null;
4073             var el = this.getEl();
4074
4075             if (attr == 'scroll') {
4076                 val = [ el.scrollLeft, el.scrollTop ];
4077             } else {
4078                 val = superclass.getAttribute.call(this, attr);
4079             }
4080
4081             return val;
4082         };
4083
4084         proto.setAttribute = function(attr, val, unit) {
4085             var el = this.getEl();
4086
4087             if (attr == 'scroll') {
4088                 el.scrollLeft = val[0];
4089                 el.scrollTop = val[1];
4090             } else {
4091                 superclass.setAttribute.call(this, attr, val, unit);
4092             }
4093         };
4094     })();
4095 /*
4096  * Based on:
4097  * Ext JS Library 1.1.1
4098  * Copyright(c) 2006-2007, Ext JS, LLC.
4099  *
4100  * Originally Released Under LGPL - original licence link has changed is not relivant.
4101  *
4102  * Fork - LGPL
4103  * <script type="text/javascript">
4104  */
4105
4106
4107 // nasty IE9 hack - what a pile of crap that is..
4108
4109  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4110     Range.prototype.createContextualFragment = function (html) {
4111         var doc = window.document;
4112         var container = doc.createElement("div");
4113         container.innerHTML = html;
4114         var frag = doc.createDocumentFragment(), n;
4115         while ((n = container.firstChild)) {
4116             frag.appendChild(n);
4117         }
4118         return frag;
4119     };
4120 }
4121
4122 /**
4123  * @class Roo.DomHelper
4124  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4125  * 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>.
4126  * @singleton
4127  */
4128 Roo.DomHelper = function(){
4129     var tempTableEl = null;
4130     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4131     var tableRe = /^table|tbody|tr|td$/i;
4132     var xmlns = {};
4133     // build as innerHTML where available
4134     /** @ignore */
4135     var createHtml = function(o){
4136         if(typeof o == 'string'){
4137             return o;
4138         }
4139         var b = "";
4140         if(!o.tag){
4141             o.tag = "div";
4142         }
4143         b += "<" + o.tag;
4144         for(var attr in o){
4145             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4146             if(attr == "style"){
4147                 var s = o["style"];
4148                 if(typeof s == "function"){
4149                     s = s.call();
4150                 }
4151                 if(typeof s == "string"){
4152                     b += ' style="' + s + '"';
4153                 }else if(typeof s == "object"){
4154                     b += ' style="';
4155                     for(var key in s){
4156                         if(typeof s[key] != "function"){
4157                             b += key + ":" + s[key] + ";";
4158                         }
4159                     }
4160                     b += '"';
4161                 }
4162             }else{
4163                 if(attr == "cls"){
4164                     b += ' class="' + o["cls"] + '"';
4165                 }else if(attr == "htmlFor"){
4166                     b += ' for="' + o["htmlFor"] + '"';
4167                 }else{
4168                     b += " " + attr + '="' + o[attr] + '"';
4169                 }
4170             }
4171         }
4172         if(emptyTags.test(o.tag)){
4173             b += "/>";
4174         }else{
4175             b += ">";
4176             var cn = o.children || o.cn;
4177             if(cn){
4178                 //http://bugs.kde.org/show_bug.cgi?id=71506
4179                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4180                     for(var i = 0, len = cn.length; i < len; i++) {
4181                         b += createHtml(cn[i], b);
4182                     }
4183                 }else{
4184                     b += createHtml(cn, b);
4185                 }
4186             }
4187             if(o.html){
4188                 b += o.html;
4189             }
4190             b += "</" + o.tag + ">";
4191         }
4192         return b;
4193     };
4194
4195     // build as dom
4196     /** @ignore */
4197     var createDom = function(o, parentNode){
4198          
4199         // defininition craeted..
4200         var ns = false;
4201         if (o.ns && o.ns != 'html') {
4202                
4203             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4204                 xmlns[o.ns] = o.xmlns;
4205                 ns = o.xmlns;
4206             }
4207             if (typeof(xmlns[o.ns]) == 'undefined') {
4208                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4209             }
4210             ns = xmlns[o.ns];
4211         }
4212         
4213         
4214         if (typeof(o) == 'string') {
4215             return parentNode.appendChild(document.createTextNode(o));
4216         }
4217         o.tag = o.tag || div;
4218         if (o.ns && Roo.isIE) {
4219             ns = false;
4220             o.tag = o.ns + ':' + o.tag;
4221             
4222         }
4223         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4224         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4225         for(var attr in o){
4226             
4227             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4228                     attr == "style" || typeof o[attr] == "function") continue;
4229                     
4230             if(attr=="cls" && Roo.isIE){
4231                 el.className = o["cls"];
4232             }else{
4233                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4234                 else el[attr] = o[attr];
4235             }
4236         }
4237         Roo.DomHelper.applyStyles(el, o.style);
4238         var cn = o.children || o.cn;
4239         if(cn){
4240             //http://bugs.kde.org/show_bug.cgi?id=71506
4241              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4242                 for(var i = 0, len = cn.length; i < len; i++) {
4243                     createDom(cn[i], el);
4244                 }
4245             }else{
4246                 createDom(cn, el);
4247             }
4248         }
4249         if(o.html){
4250             el.innerHTML = o.html;
4251         }
4252         if(parentNode){
4253            parentNode.appendChild(el);
4254         }
4255         return el;
4256     };
4257
4258     var ieTable = function(depth, s, h, e){
4259         tempTableEl.innerHTML = [s, h, e].join('');
4260         var i = -1, el = tempTableEl;
4261         while(++i < depth){
4262             el = el.firstChild;
4263         }
4264         return el;
4265     };
4266
4267     // kill repeat to save bytes
4268     var ts = '<table>',
4269         te = '</table>',
4270         tbs = ts+'<tbody>',
4271         tbe = '</tbody>'+te,
4272         trs = tbs + '<tr>',
4273         tre = '</tr>'+tbe;
4274
4275     /**
4276      * @ignore
4277      * Nasty code for IE's broken table implementation
4278      */
4279     var insertIntoTable = function(tag, where, el, html){
4280         if(!tempTableEl){
4281             tempTableEl = document.createElement('div');
4282         }
4283         var node;
4284         var before = null;
4285         if(tag == 'td'){
4286             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4287                 return;
4288             }
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292             } else{
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295             }
4296             node = ieTable(4, trs, html, tre);
4297         }
4298         else if(tag == 'tr'){
4299             if(where == 'beforebegin'){
4300                 before = el;
4301                 el = el.parentNode;
4302                 node = ieTable(3, tbs, html, tbe);
4303             } else if(where == 'afterend'){
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306                 node = ieTable(3, tbs, html, tbe);
4307             } else{ // INTO a TR
4308                 if(where == 'afterbegin'){
4309                     before = el.firstChild;
4310                 }
4311                 node = ieTable(4, trs, html, tre);
4312             }
4313         } else if(tag == 'tbody'){
4314             if(where == 'beforebegin'){
4315                 before = el;
4316                 el = el.parentNode;
4317                 node = ieTable(2, ts, html, te);
4318             } else if(where == 'afterend'){
4319                 before = el.nextSibling;
4320                 el = el.parentNode;
4321                 node = ieTable(2, ts, html, te);
4322             } else{
4323                 if(where == 'afterbegin'){
4324                     before = el.firstChild;
4325                 }
4326                 node = ieTable(3, tbs, html, tbe);
4327             }
4328         } else{ // TABLE
4329             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4330                 return;
4331             }
4332             if(where == 'afterbegin'){
4333                 before = el.firstChild;
4334             }
4335             node = ieTable(2, ts, html, te);
4336         }
4337         el.insertBefore(node, before);
4338         return node;
4339     };
4340
4341     return {
4342     /** True to force the use of DOM instead of html fragments @type Boolean */
4343     useDom : false,
4344
4345     /**
4346      * Returns the markup for the passed Element(s) config
4347      * @param {Object} o The Dom object spec (and children)
4348      * @return {String}
4349      */
4350     markup : function(o){
4351         return createHtml(o);
4352     },
4353
4354     /**
4355      * Applies a style specification to an element
4356      * @param {String/HTMLElement} el The element to apply styles to
4357      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4358      * a function which returns such a specification.
4359      */
4360     applyStyles : function(el, styles){
4361         if(styles){
4362            el = Roo.fly(el);
4363            if(typeof styles == "string"){
4364                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4365                var matches;
4366                while ((matches = re.exec(styles)) != null){
4367                    el.setStyle(matches[1], matches[2]);
4368                }
4369            }else if (typeof styles == "object"){
4370                for (var style in styles){
4371                   el.setStyle(style, styles[style]);
4372                }
4373            }else if (typeof styles == "function"){
4374                 Roo.DomHelper.applyStyles(el, styles.call());
4375            }
4376         }
4377     },
4378
4379     /**
4380      * Inserts an HTML fragment into the Dom
4381      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4382      * @param {HTMLElement} el The context element
4383      * @param {String} html The HTML fragmenet
4384      * @return {HTMLElement} The new node
4385      */
4386     insertHtml : function(where, el, html){
4387         where = where.toLowerCase();
4388         if(el.insertAdjacentHTML){
4389             if(tableRe.test(el.tagName)){
4390                 var rs;
4391                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4392                     return rs;
4393                 }
4394             }
4395             switch(where){
4396                 case "beforebegin":
4397                     el.insertAdjacentHTML('BeforeBegin', html);
4398                     return el.previousSibling;
4399                 case "afterbegin":
4400                     el.insertAdjacentHTML('AfterBegin', html);
4401                     return el.firstChild;
4402                 case "beforeend":
4403                     el.insertAdjacentHTML('BeforeEnd', html);
4404                     return el.lastChild;
4405                 case "afterend":
4406                     el.insertAdjacentHTML('AfterEnd', html);
4407                     return el.nextSibling;
4408             }
4409             throw 'Illegal insertion point -> "' + where + '"';
4410         }
4411         var range = el.ownerDocument.createRange();
4412         var frag;
4413         switch(where){
4414              case "beforebegin":
4415                 range.setStartBefore(el);
4416                 frag = range.createContextualFragment(html);
4417                 el.parentNode.insertBefore(frag, el);
4418                 return el.previousSibling;
4419              case "afterbegin":
4420                 if(el.firstChild){
4421                     range.setStartBefore(el.firstChild);
4422                     frag = range.createContextualFragment(html);
4423                     el.insertBefore(frag, el.firstChild);
4424                     return el.firstChild;
4425                 }else{
4426                     el.innerHTML = html;
4427                     return el.firstChild;
4428                 }
4429             case "beforeend":
4430                 if(el.lastChild){
4431                     range.setStartAfter(el.lastChild);
4432                     frag = range.createContextualFragment(html);
4433                     el.appendChild(frag);
4434                     return el.lastChild;
4435                 }else{
4436                     el.innerHTML = html;
4437                     return el.lastChild;
4438                 }
4439             case "afterend":
4440                 range.setStartAfter(el);
4441                 frag = range.createContextualFragment(html);
4442                 el.parentNode.insertBefore(frag, el.nextSibling);
4443                 return el.nextSibling;
4444             }
4445             throw 'Illegal insertion point -> "' + where + '"';
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them before el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertBefore : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "beforeBegin");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them after el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object} o The Dom object spec (and children)
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertAfter : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them as the first child of el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertFirst : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterBegin");
4479     },
4480
4481     // private
4482     doInsert : function(el, o, returnElement, pos, sibling){
4483         el = Roo.getDom(el);
4484         var newNode;
4485         if(this.useDom || o.ns){
4486             newNode = createDom(o, null);
4487             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4488         }else{
4489             var html = createHtml(o);
4490             newNode = this.insertHtml(pos, el, html);
4491         }
4492         return returnElement ? Roo.get(newNode, true) : newNode;
4493     },
4494
4495     /**
4496      * Creates new Dom element(s) and appends them to el
4497      * @param {String/HTMLElement/Element} el The context element
4498      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4499      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4500      * @return {HTMLElement/Roo.Element} The new node
4501      */
4502     append : function(el, o, returnElement){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.appendChild(newNode);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml("beforeEnd", el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and overwrites the contents of el with them
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     overwrite : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         if (o.ns) {
4525           
4526             while (el.childNodes.length) {
4527                 el.removeChild(el.firstChild);
4528             }
4529             createDom(o, el);
4530         } else {
4531             el.innerHTML = createHtml(o);   
4532         }
4533         
4534         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4535     },
4536
4537     /**
4538      * Creates a new Roo.DomHelper.Template from the Dom object spec
4539      * @param {Object} o The Dom object spec (and children)
4540      * @return {Roo.DomHelper.Template} The new template
4541      */
4542     createTemplate : function(o){
4543         var html = createHtml(o);
4544         return new Roo.Template(html);
4545     }
4546     };
4547 }();
4548 /*
4549  * Based on:
4550  * Ext JS Library 1.1.1
4551  * Copyright(c) 2006-2007, Ext JS, LLC.
4552  *
4553  * Originally Released Under LGPL - original licence link has changed is not relivant.
4554  *
4555  * Fork - LGPL
4556  * <script type="text/javascript">
4557  */
4558  
4559 /**
4560 * @class Roo.Template
4561 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4562 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4563 * Usage:
4564 <pre><code>
4565 var t = new Roo.Template({
4566     html :  '&lt;div name="{id}"&gt;' + 
4567         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4568         '&lt;/div&gt;',
4569     myformat: function (value, allValues) {
4570         return 'XX' + value;
4571     }
4572 });
4573 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4574 </code></pre>
4575 * For more information see this blog post with examples:
4576 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4577      - Create Elements using DOM, HTML fragments and Templates</a>. 
4578 * @constructor
4579 * @param {Object} cfg - Configuration object.
4580 */
4581 Roo.Template = function(cfg){
4582     // BC!
4583     if(cfg instanceof Array){
4584         cfg = cfg.join("");
4585     }else if(arguments.length > 1){
4586         cfg = Array.prototype.join.call(arguments, "");
4587     }
4588     
4589     
4590     if (typeof(cfg) == 'object') {
4591         Roo.apply(this,cfg)
4592     } else {
4593         // bc
4594         this.html = cfg;
4595     }
4596     if (this.url) {
4597         this.load();
4598     }
4599     
4600 };
4601 Roo.Template.prototype = {
4602     
4603     /**
4604      * @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..
4605      *                    it should be fixed so that template is observable...
4606      */
4607     url : false,
4608     /**
4609      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4610      */
4611     html : '',
4612     /**
4613      * Returns an HTML fragment of this template with the specified values applied.
4614      * @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'})
4615      * @return {String} The HTML fragment
4616      */
4617     applyTemplate : function(values){
4618         try {
4619            
4620             if(this.compiled){
4621                 return this.compiled(values);
4622             }
4623             var useF = this.disableFormats !== true;
4624             var fm = Roo.util.Format, tpl = this;
4625             var fn = function(m, name, format, args){
4626                 if(format && useF){
4627                     if(format.substr(0, 5) == "this."){
4628                         return tpl.call(format.substr(5), values[name], values);
4629                     }else{
4630                         if(args){
4631                             // quoted values are required for strings in compiled templates, 
4632                             // but for non compiled we need to strip them
4633                             // quoted reversed for jsmin
4634                             var re = /^\s*['"](.*)["']\s*$/;
4635                             args = args.split(',');
4636                             for(var i = 0, len = args.length; i < len; i++){
4637                                 args[i] = args[i].replace(re, "$1");
4638                             }
4639                             args = [values[name]].concat(args);
4640                         }else{
4641                             args = [values[name]];
4642                         }
4643                         return fm[format].apply(fm, args);
4644                     }
4645                 }else{
4646                     return values[name] !== undefined ? values[name] : "";
4647                 }
4648             };
4649             return this.html.replace(this.re, fn);
4650         } catch (e) {
4651             Roo.log(e);
4652             throw e;
4653         }
4654          
4655     },
4656     
4657     loading : false,
4658       
4659     load : function ()
4660     {
4661          
4662         if (this.loading) {
4663             return;
4664         }
4665         var _t = this;
4666         
4667         this.loading = true;
4668         this.compiled = false;
4669         
4670         var cx = new Roo.data.Connection();
4671         cx.request({
4672             url : this.url,
4673             method : 'GET',
4674             success : function (response) {
4675                 _t.loading = false;
4676                 _t.html = response.responseText;
4677                 _t.url = false;
4678                 _t.compile();
4679              },
4680             failure : function(response) {
4681                 Roo.log("Template failed to load from " + _t.url);
4682                 _t.loading = false;
4683             }
4684         });
4685     },
4686
4687     /**
4688      * Sets the HTML used as the template and optionally compiles it.
4689      * @param {String} html
4690      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4691      * @return {Roo.Template} this
4692      */
4693     set : function(html, compile){
4694         this.html = html;
4695         this.compiled = null;
4696         if(compile){
4697             this.compile();
4698         }
4699         return this;
4700     },
4701     
4702     /**
4703      * True to disable format functions (defaults to false)
4704      * @type Boolean
4705      */
4706     disableFormats : false,
4707     
4708     /**
4709     * The regular expression used to match template variables 
4710     * @type RegExp
4711     * @property 
4712     */
4713     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4714     
4715     /**
4716      * Compiles the template into an internal function, eliminating the RegEx overhead.
4717      * @return {Roo.Template} this
4718      */
4719     compile : function(){
4720         var fm = Roo.util.Format;
4721         var useF = this.disableFormats !== true;
4722         var sep = Roo.isGecko ? "+" : ",";
4723         var fn = function(m, name, format, args){
4724             if(format && useF){
4725                 args = args ? ',' + args : "";
4726                 if(format.substr(0, 5) != "this."){
4727                     format = "fm." + format + '(';
4728                 }else{
4729                     format = 'this.call("'+ format.substr(5) + '", ';
4730                     args = ", values";
4731                 }
4732             }else{
4733                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4734             }
4735             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4736         };
4737         var body;
4738         // branched to use + in gecko and [].join() in others
4739         if(Roo.isGecko){
4740             body = "this.compiled = function(values){ return '" +
4741                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4742                     "';};";
4743         }else{
4744             body = ["this.compiled = function(values){ return ['"];
4745             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4746             body.push("'].join('');};");
4747             body = body.join('');
4748         }
4749         /**
4750          * eval:var:values
4751          * eval:var:fm
4752          */
4753         eval(body);
4754         return this;
4755     },
4756     
4757     // private function used to call members
4758     call : function(fnName, value, allValues){
4759         return this[fnName](value, allValues);
4760     },
4761     
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertFirst: function(el, values, returnElement){
4770         return this.doInsert('afterBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) before el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertBefore: function(el, values, returnElement){
4781         return this.doInsert('beforeBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) after el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertAfter : function(el, values, returnElement){
4792         return this.doInsert('afterEnd', el, values, returnElement);
4793     },
4794     
4795     /**
4796      * Applies the supplied values to the template and appends the new node(s) to el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @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'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     append : function(el, values, returnElement){
4803         return this.doInsert('beforeEnd', el, values, returnElement);
4804     },
4805
4806     doInsert : function(where, el, values, returnEl){
4807         el = Roo.getDom(el);
4808         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4809         return returnEl ? Roo.get(newNode, true) : newNode;
4810     },
4811
4812     /**
4813      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4814      * @param {String/HTMLElement/Roo.Element} el The context element
4815      * @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'})
4816      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4817      * @return {HTMLElement/Roo.Element} The new node or Element
4818      */
4819     overwrite : function(el, values, returnElement){
4820         el = Roo.getDom(el);
4821         el.innerHTML = this.applyTemplate(values);
4822         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4823     }
4824 };
4825 /**
4826  * Alias for {@link #applyTemplate}
4827  * @method
4828  */
4829 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4830
4831 // backwards compat
4832 Roo.DomHelper.Template = Roo.Template;
4833
4834 /**
4835  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4836  * @param {String/HTMLElement} el A DOM element or its id
4837  * @returns {Roo.Template} The created template
4838  * @static
4839  */
4840 Roo.Template.from = function(el){
4841     el = Roo.getDom(el);
4842     return new Roo.Template(el.value || el.innerHTML);
4843 };/*
4844  * Based on:
4845  * Ext JS Library 1.1.1
4846  * Copyright(c) 2006-2007, Ext JS, LLC.
4847  *
4848  * Originally Released Under LGPL - original licence link has changed is not relivant.
4849  *
4850  * Fork - LGPL
4851  * <script type="text/javascript">
4852  */
4853  
4854
4855 /*
4856  * This is code is also distributed under MIT license for use
4857  * with jQuery and prototype JavaScript libraries.
4858  */
4859 /**
4860  * @class Roo.DomQuery
4861 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).
4862 <p>
4863 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>
4864
4865 <p>
4866 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.
4867 </p>
4868 <h4>Element Selectors:</h4>
4869 <ul class="list">
4870     <li> <b>*</b> any element</li>
4871     <li> <b>E</b> an element with the tag E</li>
4872     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4873     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4874     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4875     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4876 </ul>
4877 <h4>Attribute Selectors:</h4>
4878 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4879 <ul class="list">
4880     <li> <b>E[foo]</b> has an attribute "foo"</li>
4881     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4882     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4883     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4884     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4885     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4886     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4887 </ul>
4888 <h4>Pseudo Classes:</h4>
4889 <ul class="list">
4890     <li> <b>E:first-child</b> E is the first child of its parent</li>
4891     <li> <b>E:last-child</b> E is the last child of its parent</li>
4892     <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>
4893     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4894     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4895     <li> <b>E:only-child</b> E is the only child of its parent</li>
4896     <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>
4897     <li> <b>E:first</b> the first E in the resultset</li>
4898     <li> <b>E:last</b> the last E in the resultset</li>
4899     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4900     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4901     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4902     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4903     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4904     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4905     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4906     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4907     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4908 </ul>
4909 <h4>CSS Value Selectors:</h4>
4910 <ul class="list">
4911     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4912     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4913     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4914     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4915     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4916     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4917 </ul>
4918  * @singleton
4919  */
4920 Roo.DomQuery = function(){
4921     var cache = {}, simpleCache = {}, valueCache = {};
4922     var nonSpace = /\S/;
4923     var trimRe = /^\s+|\s+$/g;
4924     var tplRe = /\{(\d+)\}/g;
4925     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4926     var tagTokenRe = /^(#)?([\w-\*]+)/;
4927     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4928
4929     function child(p, index){
4930         var i = 0;
4931         var n = p.firstChild;
4932         while(n){
4933             if(n.nodeType == 1){
4934                if(++i == index){
4935                    return n;
4936                }
4937             }
4938             n = n.nextSibling;
4939         }
4940         return null;
4941     };
4942
4943     function next(n){
4944         while((n = n.nextSibling) && n.nodeType != 1);
4945         return n;
4946     };
4947
4948     function prev(n){
4949         while((n = n.previousSibling) && n.nodeType != 1);
4950         return n;
4951     };
4952
4953     function children(d){
4954         var n = d.firstChild, ni = -1;
4955             while(n){
4956                 var nx = n.nextSibling;
4957                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4958                     d.removeChild(n);
4959                 }else{
4960                     n.nodeIndex = ++ni;
4961                 }
4962                 n = nx;
4963             }
4964             return this;
4965         };
4966
4967     function byClassName(c, a, v){
4968         if(!v){
4969             return c;
4970         }
4971         var r = [], ri = -1, cn;
4972         for(var i = 0, ci; ci = c[i]; i++){
4973             if((' '+ci.className+' ').indexOf(v) != -1){
4974                 r[++ri] = ci;
4975             }
4976         }
4977         return r;
4978     };
4979
4980     function attrValue(n, attr){
4981         if(!n.tagName && typeof n.length != "undefined"){
4982             n = n[0];
4983         }
4984         if(!n){
4985             return null;
4986         }
4987         if(attr == "for"){
4988             return n.htmlFor;
4989         }
4990         if(attr == "class" || attr == "className"){
4991             return n.className;
4992         }
4993         return n.getAttribute(attr) || n[attr];
4994
4995     };
4996
4997     function getNodes(ns, mode, tagName){
4998         var result = [], ri = -1, cs;
4999         if(!ns){
5000             return result;
5001         }
5002         tagName = tagName || "*";
5003         if(typeof ns.getElementsByTagName != "undefined"){
5004             ns = [ns];
5005         }
5006         if(!mode){
5007             for(var i = 0, ni; ni = ns[i]; i++){
5008                 cs = ni.getElementsByTagName(tagName);
5009                 for(var j = 0, ci; ci = cs[j]; j++){
5010                     result[++ri] = ci;
5011                 }
5012             }
5013         }else if(mode == "/" || mode == ">"){
5014             var utag = tagName.toUpperCase();
5015             for(var i = 0, ni, cn; ni = ns[i]; i++){
5016                 cn = ni.children || ni.childNodes;
5017                 for(var j = 0, cj; cj = cn[j]; j++){
5018                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5019                         result[++ri] = cj;
5020                     }
5021                 }
5022             }
5023         }else if(mode == "+"){
5024             var utag = tagName.toUpperCase();
5025             for(var i = 0, n; n = ns[i]; i++){
5026                 while((n = n.nextSibling) && n.nodeType != 1);
5027                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5028                     result[++ri] = n;
5029                 }
5030             }
5031         }else if(mode == "~"){
5032             for(var i = 0, n; n = ns[i]; i++){
5033                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5034                 if(n){
5035                     result[++ri] = n;
5036                 }
5037             }
5038         }
5039         return result;
5040     };
5041
5042     function concat(a, b){
5043         if(b.slice){
5044             return a.concat(b);
5045         }
5046         for(var i = 0, l = b.length; i < l; i++){
5047             a[a.length] = b[i];
5048         }
5049         return a;
5050     }
5051
5052     function byTag(cs, tagName){
5053         if(cs.tagName || cs == document){
5054             cs = [cs];
5055         }
5056         if(!tagName){
5057             return cs;
5058         }
5059         var r = [], ri = -1;
5060         tagName = tagName.toLowerCase();
5061         for(var i = 0, ci; ci = cs[i]; i++){
5062             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5063                 r[++ri] = ci;
5064             }
5065         }
5066         return r;
5067     };
5068
5069     function byId(cs, attr, id){
5070         if(cs.tagName || cs == document){
5071             cs = [cs];
5072         }
5073         if(!id){
5074             return cs;
5075         }
5076         var r = [], ri = -1;
5077         for(var i = 0,ci; ci = cs[i]; i++){
5078             if(ci && ci.id == id){
5079                 r[++ri] = ci;
5080                 return r;
5081             }
5082         }
5083         return r;
5084     };
5085
5086     function byAttribute(cs, attr, value, op, custom){
5087         var r = [], ri = -1, st = custom=="{";
5088         var f = Roo.DomQuery.operators[op];
5089         for(var i = 0, ci; ci = cs[i]; i++){
5090             var a;
5091             if(st){
5092                 a = Roo.DomQuery.getStyle(ci, attr);
5093             }
5094             else if(attr == "class" || attr == "className"){
5095                 a = ci.className;
5096             }else if(attr == "for"){
5097                 a = ci.htmlFor;
5098             }else if(attr == "href"){
5099                 a = ci.getAttribute("href", 2);
5100             }else{
5101                 a = ci.getAttribute(attr);
5102             }
5103             if((f && f(a, value)) || (!f && a)){
5104                 r[++ri] = ci;
5105             }
5106         }
5107         return r;
5108     };
5109
5110     function byPseudo(cs, name, value){
5111         return Roo.DomQuery.pseudos[name](cs, value);
5112     };
5113
5114     // This is for IE MSXML which does not support expandos.
5115     // IE runs the same speed using setAttribute, however FF slows way down
5116     // and Safari completely fails so they need to continue to use expandos.
5117     var isIE = window.ActiveXObject ? true : false;
5118
5119     // this eval is stop the compressor from
5120     // renaming the variable to something shorter
5121     
5122     /** eval:var:batch */
5123     var batch = 30803; 
5124
5125     var key = 30803;
5126
5127     function nodupIEXml(cs){
5128         var d = ++key;
5129         cs[0].setAttribute("_nodup", d);
5130         var r = [cs[0]];
5131         for(var i = 1, len = cs.length; i < len; i++){
5132             var c = cs[i];
5133             if(!c.getAttribute("_nodup") != d){
5134                 c.setAttribute("_nodup", d);
5135                 r[r.length] = c;
5136             }
5137         }
5138         for(var i = 0, len = cs.length; i < len; i++){
5139             cs[i].removeAttribute("_nodup");
5140         }
5141         return r;
5142     }
5143
5144     function nodup(cs){
5145         if(!cs){
5146             return [];
5147         }
5148         var len = cs.length, c, i, r = cs, cj, ri = -1;
5149         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5150             return cs;
5151         }
5152         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5153             return nodupIEXml(cs);
5154         }
5155         var d = ++key;
5156         cs[0]._nodup = d;
5157         for(i = 1; c = cs[i]; i++){
5158             if(c._nodup != d){
5159                 c._nodup = d;
5160             }else{
5161                 r = [];
5162                 for(var j = 0; j < i; j++){
5163                     r[++ri] = cs[j];
5164                 }
5165                 for(j = i+1; cj = cs[j]; j++){
5166                     if(cj._nodup != d){
5167                         cj._nodup = d;
5168                         r[++ri] = cj;
5169                     }
5170                 }
5171                 return r;
5172             }
5173         }
5174         return r;
5175     }
5176
5177     function quickDiffIEXml(c1, c2){
5178         var d = ++key;
5179         for(var i = 0, len = c1.length; i < len; i++){
5180             c1[i].setAttribute("_qdiff", d);
5181         }
5182         var r = [];
5183         for(var i = 0, len = c2.length; i < len; i++){
5184             if(c2[i].getAttribute("_qdiff") != d){
5185                 r[r.length] = c2[i];
5186             }
5187         }
5188         for(var i = 0, len = c1.length; i < len; i++){
5189            c1[i].removeAttribute("_qdiff");
5190         }
5191         return r;
5192     }
5193
5194     function quickDiff(c1, c2){
5195         var len1 = c1.length;
5196         if(!len1){
5197             return c2;
5198         }
5199         if(isIE && c1[0].selectSingleNode){
5200             return quickDiffIEXml(c1, c2);
5201         }
5202         var d = ++key;
5203         for(var i = 0; i < len1; i++){
5204             c1[i]._qdiff = d;
5205         }
5206         var r = [];
5207         for(var i = 0, len = c2.length; i < len; i++){
5208             if(c2[i]._qdiff != d){
5209                 r[r.length] = c2[i];
5210             }
5211         }
5212         return r;
5213     }
5214
5215     function quickId(ns, mode, root, id){
5216         if(ns == root){
5217            var d = root.ownerDocument || root;
5218            return d.getElementById(id);
5219         }
5220         ns = getNodes(ns, mode, "*");
5221         return byId(ns, null, id);
5222     }
5223
5224     return {
5225         getStyle : function(el, name){
5226             return Roo.fly(el).getStyle(name);
5227         },
5228         /**
5229          * Compiles a selector/xpath query into a reusable function. The returned function
5230          * takes one parameter "root" (optional), which is the context node from where the query should start.
5231          * @param {String} selector The selector/xpath query
5232          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5233          * @return {Function}
5234          */
5235         compile : function(path, type){
5236             type = type || "select";
5237             
5238             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5239             var q = path, mode, lq;
5240             var tk = Roo.DomQuery.matchers;
5241             var tklen = tk.length;
5242             var mm;
5243
5244             // accept leading mode switch
5245             var lmode = q.match(modeRe);
5246             if(lmode && lmode[1]){
5247                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5248                 q = q.replace(lmode[1], "");
5249             }
5250             // strip leading slashes
5251             while(path.substr(0, 1)=="/"){
5252                 path = path.substr(1);
5253             }
5254
5255             while(q && lq != q){
5256                 lq = q;
5257                 var tm = q.match(tagTokenRe);
5258                 if(type == "select"){
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }else if(q.substr(0, 1) != '@'){
5267                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5268                     }
5269                 }else{
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }
5278                 }
5279                 while(!(mm = q.match(modeRe))){
5280                     var matched = false;
5281                     for(var j = 0; j < tklen; j++){
5282                         var t = tk[j];
5283                         var m = q.match(t.re);
5284                         if(m){
5285                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5286                                                     return m[i];
5287                                                 });
5288                             q = q.replace(m[0], "");
5289                             matched = true;
5290                             break;
5291                         }
5292                     }
5293                     // prevent infinite loop on bad selector
5294                     if(!matched){
5295                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5296                     }
5297                 }
5298                 if(mm[1]){
5299                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5300                     q = q.replace(mm[1], "");
5301                 }
5302             }
5303             fn[fn.length] = "return nodup(n);\n}";
5304             
5305              /** 
5306               * list of variables that need from compression as they are used by eval.
5307              *  eval:var:batch 
5308              *  eval:var:nodup
5309              *  eval:var:byTag
5310              *  eval:var:ById
5311              *  eval:var:getNodes
5312              *  eval:var:quickId
5313              *  eval:var:mode
5314              *  eval:var:root
5315              *  eval:var:n
5316              *  eval:var:byClassName
5317              *  eval:var:byPseudo
5318              *  eval:var:byAttribute
5319              *  eval:var:attrValue
5320              * 
5321              **/ 
5322             eval(fn.join(""));
5323             return f;
5324         },
5325
5326         /**
5327          * Selects a group of elements.
5328          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5329          * @param {Node} root (optional) The start of the query (defaults to document).
5330          * @return {Array}
5331          */
5332         select : function(path, root, type){
5333             if(!root || root == document){
5334                 root = document;
5335             }
5336             if(typeof root == "string"){
5337                 root = document.getElementById(root);
5338             }
5339             var paths = path.split(",");
5340             var results = [];
5341             for(var i = 0, len = paths.length; i < len; i++){
5342                 var p = paths[i].replace(trimRe, "");
5343                 if(!cache[p]){
5344                     cache[p] = Roo.DomQuery.compile(p);
5345                     if(!cache[p]){
5346                         throw p + " is not a valid selector";
5347                     }
5348                 }
5349                 var result = cache[p](root);
5350                 if(result && result != document){
5351                     results = results.concat(result);
5352                 }
5353             }
5354             if(paths.length > 1){
5355                 return nodup(results);
5356             }
5357             return results;
5358         },
5359
5360         /**
5361          * Selects a single element.
5362          * @param {String} selector The selector/xpath query
5363          * @param {Node} root (optional) The start of the query (defaults to document).
5364          * @return {Element}
5365          */
5366         selectNode : function(path, root){
5367             return Roo.DomQuery.select(path, root)[0];
5368         },
5369
5370         /**
5371          * Selects the value of a node, optionally replacing null with the defaultValue.
5372          * @param {String} selector The selector/xpath query
5373          * @param {Node} root (optional) The start of the query (defaults to document).
5374          * @param {String} defaultValue
5375          */
5376         selectValue : function(path, root, defaultValue){
5377             path = path.replace(trimRe, "");
5378             if(!valueCache[path]){
5379                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5380             }
5381             var n = valueCache[path](root);
5382             n = n[0] ? n[0] : n;
5383             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5384             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5385         },
5386
5387         /**
5388          * Selects the value of a node, parsing integers and floats.
5389          * @param {String} selector The selector/xpath query
5390          * @param {Node} root (optional) The start of the query (defaults to document).
5391          * @param {Number} defaultValue
5392          * @return {Number}
5393          */
5394         selectNumber : function(path, root, defaultValue){
5395             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5396             return parseFloat(v);
5397         },
5398
5399         /**
5400          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5401          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5402          * @param {String} selector The simple selector to test
5403          * @return {Boolean}
5404          */
5405         is : function(el, ss){
5406             if(typeof el == "string"){
5407                 el = document.getElementById(el);
5408             }
5409             var isArray = (el instanceof Array);
5410             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5411             return isArray ? (result.length == el.length) : (result.length > 0);
5412         },
5413
5414         /**
5415          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5416          * @param {Array} el An array of elements to filter
5417          * @param {String} selector The simple selector to test
5418          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5419          * the selector instead of the ones that match
5420          * @return {Array}
5421          */
5422         filter : function(els, ss, nonMatches){
5423             ss = ss.replace(trimRe, "");
5424             if(!simpleCache[ss]){
5425                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5426             }
5427             var result = simpleCache[ss](els);
5428             return nonMatches ? quickDiff(result, els) : result;
5429         },
5430
5431         /**
5432          * Collection of matching regular expressions and code snippets.
5433          */
5434         matchers : [{
5435                 re: /^\.([\w-]+)/,
5436                 select: 'n = byClassName(n, null, " {1} ");'
5437             }, {
5438                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5439                 select: 'n = byPseudo(n, "{1}", "{2}");'
5440             },{
5441                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5442                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5443             }, {
5444                 re: /^#([\w-]+)/,
5445                 select: 'n = byId(n, null, "{1}");'
5446             },{
5447                 re: /^@([\w-]+)/,
5448                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5449             }
5450         ],
5451
5452         /**
5453          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5454          * 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;.
5455          */
5456         operators : {
5457             "=" : function(a, v){
5458                 return a == v;
5459             },
5460             "!=" : function(a, v){
5461                 return a != v;
5462             },
5463             "^=" : function(a, v){
5464                 return a && a.substr(0, v.length) == v;
5465             },
5466             "$=" : function(a, v){
5467                 return a && a.substr(a.length-v.length) == v;
5468             },
5469             "*=" : function(a, v){
5470                 return a && a.indexOf(v) !== -1;
5471             },
5472             "%=" : function(a, v){
5473                 return (a % v) == 0;
5474             },
5475             "|=" : function(a, v){
5476                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5477             },
5478             "~=" : function(a, v){
5479                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5480             }
5481         },
5482
5483         /**
5484          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5485          * and the argument (if any) supplied in the selector.
5486          */
5487         pseudos : {
5488             "first-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.previousSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "last-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.nextSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "nth-child" : function(c, a) {
5511                 var r = [], ri = -1;
5512                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5513                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5514                 for(var i = 0, n; n = c[i]; i++){
5515                     var pn = n.parentNode;
5516                     if (batch != pn._batch) {
5517                         var j = 0;
5518                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5519                             if(cn.nodeType == 1){
5520                                cn.nodeIndex = ++j;
5521                             }
5522                         }
5523                         pn._batch = batch;
5524                     }
5525                     if (f == 1) {
5526                         if (l == 0 || n.nodeIndex == l){
5527                             r[++ri] = n;
5528                         }
5529                     } else if ((n.nodeIndex + l) % f == 0){
5530                         r[++ri] = n;
5531                     }
5532                 }
5533
5534                 return r;
5535             },
5536
5537             "only-child" : function(c){
5538                 var r = [], ri = -1;;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(!prev(ci) && !next(ci)){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "empty" : function(c){
5548                 var r = [], ri = -1;
5549                 for(var i = 0, ci; ci = c[i]; i++){
5550                     var cns = ci.childNodes, j = 0, cn, empty = true;
5551                     while(cn = cns[j]){
5552                         ++j;
5553                         if(cn.nodeType == 1 || cn.nodeType == 3){
5554                             empty = false;
5555                             break;
5556                         }
5557                     }
5558                     if(empty){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             },
5564
5565             "contains" : function(c, v){
5566                 var r = [], ri = -1;
5567                 for(var i = 0, ci; ci = c[i]; i++){
5568                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5569                         r[++ri] = ci;
5570                     }
5571                 }
5572                 return r;
5573             },
5574
5575             "nodeValue" : function(c, v){
5576                 var r = [], ri = -1;
5577                 for(var i = 0, ci; ci = c[i]; i++){
5578                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "checked" : function(c){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if(ci.checked == true){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "not" : function(c, ss){
5596                 return Roo.DomQuery.filter(c, ss, true);
5597             },
5598
5599             "odd" : function(c){
5600                 return this["nth-child"](c, "odd");
5601             },
5602
5603             "even" : function(c){
5604                 return this["nth-child"](c, "even");
5605             },
5606
5607             "nth" : function(c, a){
5608                 return c[a-1] || [];
5609             },
5610
5611             "first" : function(c){
5612                 return c[0] || [];
5613             },
5614
5615             "last" : function(c){
5616                 return c[c.length-1] || [];
5617             },
5618
5619             "has" : function(c, ss){
5620                 var s = Roo.DomQuery.select;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     if(s(ss, ci).length > 0){
5624                         r[++ri] = ci;
5625                     }
5626                 }
5627                 return r;
5628             },
5629
5630             "next" : function(c, ss){
5631                 var is = Roo.DomQuery.is;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     var n = next(ci);
5635                     if(n && is(n, ss)){
5636                         r[++ri] = ci;
5637                     }
5638                 }
5639                 return r;
5640             },
5641
5642             "prev" : function(c, ss){
5643                 var is = Roo.DomQuery.is;
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var n = prev(ci);
5647                     if(n && is(n, ss)){
5648                         r[++ri] = ci;
5649                     }
5650                 }
5651                 return r;
5652             }
5653         }
5654     };
5655 }();
5656
5657 /**
5658  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5659  * @param {String} path The selector/xpath query
5660  * @param {Node} root (optional) The start of the query (defaults to document).
5661  * @return {Array}
5662  * @member Roo
5663  * @method query
5664  */
5665 Roo.query = Roo.DomQuery.select;
5666 /*
5667  * Based on:
5668  * Ext JS Library 1.1.1
5669  * Copyright(c) 2006-2007, Ext JS, LLC.
5670  *
5671  * Originally Released Under LGPL - original licence link has changed is not relivant.
5672  *
5673  * Fork - LGPL
5674  * <script type="text/javascript">
5675  */
5676
5677 /**
5678  * @class Roo.util.Observable
5679  * Base class that provides a common interface for publishing events. Subclasses are expected to
5680  * to have a property "events" with all the events defined.<br>
5681  * For example:
5682  * <pre><code>
5683  Employee = function(name){
5684     this.name = name;
5685     this.addEvents({
5686         "fired" : true,
5687         "quit" : true
5688     });
5689  }
5690  Roo.extend(Employee, Roo.util.Observable);
5691 </code></pre>
5692  * @param {Object} config properties to use (incuding events / listeners)
5693  */
5694
5695 Roo.util.Observable = function(cfg){
5696     
5697     cfg = cfg|| {};
5698     this.addEvents(cfg.events || {});
5699     if (cfg.events) {
5700         delete cfg.events; // make sure
5701     }
5702      
5703     Roo.apply(this, cfg);
5704     
5705     if(this.listeners){
5706         this.on(this.listeners);
5707         delete this.listeners;
5708     }
5709 };
5710 Roo.util.Observable.prototype = {
5711     /** 
5712  * @cfg {Object} listeners  list of events and functions to call for this object, 
5713  * For example :
5714  * <pre><code>
5715     listeners :  { 
5716        'click' : function(e) {
5717            ..... 
5718         } ,
5719         .... 
5720     } 
5721   </code></pre>
5722  */
5723     
5724     
5725     /**
5726      * Fires the specified event with the passed parameters (minus the event name).
5727      * @param {String} eventName
5728      * @param {Object...} args Variable number of parameters are passed to handlers
5729      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5730      */
5731     fireEvent : function(){
5732         var ce = this.events[arguments[0].toLowerCase()];
5733         if(typeof ce == "object"){
5734             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5735         }else{
5736             return true;
5737         }
5738     },
5739
5740     // private
5741     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5742
5743     /**
5744      * Appends an event handler to this component
5745      * @param {String}   eventName The type of event to listen for
5746      * @param {Function} handler The method the event invokes
5747      * @param {Object}   scope (optional) The scope in which to execute the handler
5748      * function. The handler function's "this" context.
5749      * @param {Object}   options (optional) An object containing handler configuration
5750      * properties. This may contain any of the following properties:<ul>
5751      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5752      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5753      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5754      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5755      * by the specified number of milliseconds. If the event fires again within that time, the original
5756      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5757      * </ul><br>
5758      * <p>
5759      * <b>Combining Options</b><br>
5760      * Using the options argument, it is possible to combine different types of listeners:<br>
5761      * <br>
5762      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5763                 <pre><code>
5764                 el.on('click', this.onClick, this, {
5765                         single: true,
5766                 delay: 100,
5767                 forumId: 4
5768                 });
5769                 </code></pre>
5770      * <p>
5771      * <b>Attaching multiple handlers in 1 call</b><br>
5772      * The method also allows for a single argument to be passed which is a config object containing properties
5773      * which specify multiple handlers.
5774      * <pre><code>
5775                 el.on({
5776                         'click': {
5777                         fn: this.onClick,
5778                         scope: this,
5779                         delay: 100
5780                 }, 
5781                 'mouseover': {
5782                         fn: this.onMouseOver,
5783                         scope: this
5784                 },
5785                 'mouseout': {
5786                         fn: this.onMouseOut,
5787                         scope: this
5788                 }
5789                 });
5790                 </code></pre>
5791      * <p>
5792      * Or a shorthand syntax which passes the same scope object to all handlers:
5793         <pre><code>
5794                 el.on({
5795                         'click': this.onClick,
5796                 'mouseover': this.onMouseOver,
5797                 'mouseout': this.onMouseOut,
5798                 scope: this
5799                 });
5800                 </code></pre>
5801      */
5802     addListener : function(eventName, fn, scope, o){
5803         if(typeof eventName == "object"){
5804             o = eventName;
5805             for(var e in o){
5806                 if(this.filterOptRe.test(e)){
5807                     continue;
5808                 }
5809                 if(typeof o[e] == "function"){
5810                     // shared options
5811                     this.addListener(e, o[e], o.scope,  o);
5812                 }else{
5813                     // individual options
5814                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5815                 }
5816             }
5817             return;
5818         }
5819         o = (!o || typeof o == "boolean") ? {} : o;
5820         eventName = eventName.toLowerCase();
5821         var ce = this.events[eventName] || true;
5822         if(typeof ce == "boolean"){
5823             ce = new Roo.util.Event(this, eventName);
5824             this.events[eventName] = ce;
5825         }
5826         ce.addListener(fn, scope, o);
5827     },
5828
5829     /**
5830      * Removes a listener
5831      * @param {String}   eventName     The type of event to listen for
5832      * @param {Function} handler        The handler to remove
5833      * @param {Object}   scope  (optional) The scope (this object) for the handler
5834      */
5835     removeListener : function(eventName, fn, scope){
5836         var ce = this.events[eventName.toLowerCase()];
5837         if(typeof ce == "object"){
5838             ce.removeListener(fn, scope);
5839         }
5840     },
5841
5842     /**
5843      * Removes all listeners for this object
5844      */
5845     purgeListeners : function(){
5846         for(var evt in this.events){
5847             if(typeof this.events[evt] == "object"){
5848                  this.events[evt].clearListeners();
5849             }
5850         }
5851     },
5852
5853     relayEvents : function(o, events){
5854         var createHandler = function(ename){
5855             return function(){
5856                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5857             };
5858         };
5859         for(var i = 0, len = events.length; i < len; i++){
5860             var ename = events[i];
5861             if(!this.events[ename]){ this.events[ename] = true; };
5862             o.on(ename, createHandler(ename), this);
5863         }
5864     },
5865
5866     /**
5867      * Used to define events on this Observable
5868      * @param {Object} object The object with the events defined
5869      */
5870     addEvents : function(o){
5871         if(!this.events){
5872             this.events = {};
5873         }
5874         Roo.applyIf(this.events, o);
5875     },
5876
5877     /**
5878      * Checks to see if this object has any listeners for a specified event
5879      * @param {String} eventName The name of the event to check for
5880      * @return {Boolean} True if the event is being listened for, else false
5881      */
5882     hasListener : function(eventName){
5883         var e = this.events[eventName];
5884         return typeof e == "object" && e.listeners.length > 0;
5885     }
5886 };
5887 /**
5888  * Appends an event handler to this element (shorthand for addListener)
5889  * @param {String}   eventName     The type of event to listen for
5890  * @param {Function} handler        The method the event invokes
5891  * @param {Object}   scope (optional) The scope in which to execute the handler
5892  * function. The handler function's "this" context.
5893  * @param {Object}   options  (optional)
5894  * @method
5895  */
5896 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5897 /**
5898  * Removes a listener (shorthand for removeListener)
5899  * @param {String}   eventName     The type of event to listen for
5900  * @param {Function} handler        The handler to remove
5901  * @param {Object}   scope  (optional) The scope (this object) for the handler
5902  * @method
5903  */
5904 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5905
5906 /**
5907  * Starts capture on the specified Observable. All events will be passed
5908  * to the supplied function with the event name + standard signature of the event
5909  * <b>before</b> the event is fired. If the supplied function returns false,
5910  * the event will not fire.
5911  * @param {Observable} o The Observable to capture
5912  * @param {Function} fn The function to call
5913  * @param {Object} scope (optional) The scope (this object) for the fn
5914  * @static
5915  */
5916 Roo.util.Observable.capture = function(o, fn, scope){
5917     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5918 };
5919
5920 /**
5921  * Removes <b>all</b> added captures from the Observable.
5922  * @param {Observable} o The Observable to release
5923  * @static
5924  */
5925 Roo.util.Observable.releaseCapture = function(o){
5926     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5927 };
5928
5929 (function(){
5930
5931     var createBuffered = function(h, o, scope){
5932         var task = new Roo.util.DelayedTask();
5933         return function(){
5934             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5935         };
5936     };
5937
5938     var createSingle = function(h, e, fn, scope){
5939         return function(){
5940             e.removeListener(fn, scope);
5941             return h.apply(scope, arguments);
5942         };
5943     };
5944
5945     var createDelayed = function(h, o, scope){
5946         return function(){
5947             var args = Array.prototype.slice.call(arguments, 0);
5948             setTimeout(function(){
5949                 h.apply(scope, args);
5950             }, o.delay || 10);
5951         };
5952     };
5953
5954     Roo.util.Event = function(obj, name){
5955         this.name = name;
5956         this.obj = obj;
5957         this.listeners = [];
5958     };
5959
5960     Roo.util.Event.prototype = {
5961         addListener : function(fn, scope, options){
5962             var o = options || {};
5963             scope = scope || this.obj;
5964             if(!this.isListening(fn, scope)){
5965                 var l = {fn: fn, scope: scope, options: o};
5966                 var h = fn;
5967                 if(o.delay){
5968                     h = createDelayed(h, o, scope);
5969                 }
5970                 if(o.single){
5971                     h = createSingle(h, this, fn, scope);
5972                 }
5973                 if(o.buffer){
5974                     h = createBuffered(h, o, scope);
5975                 }
5976                 l.fireFn = h;
5977                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5978                     this.listeners.push(l);
5979                 }else{
5980                     this.listeners = this.listeners.slice(0);
5981                     this.listeners.push(l);
5982                 }
5983             }
5984         },
5985
5986         findListener : function(fn, scope){
5987             scope = scope || this.obj;
5988             var ls = this.listeners;
5989             for(var i = 0, len = ls.length; i < len; i++){
5990                 var l = ls[i];
5991                 if(l.fn == fn && l.scope == scope){
5992                     return i;
5993                 }
5994             }
5995             return -1;
5996         },
5997
5998         isListening : function(fn, scope){
5999             return this.findListener(fn, scope) != -1;
6000         },
6001
6002         removeListener : function(fn, scope){
6003             var index;
6004             if((index = this.findListener(fn, scope)) != -1){
6005                 if(!this.firing){
6006                     this.listeners.splice(index, 1);
6007                 }else{
6008                     this.listeners = this.listeners.slice(0);
6009                     this.listeners.splice(index, 1);
6010                 }
6011                 return true;
6012             }
6013             return false;
6014         },
6015
6016         clearListeners : function(){
6017             this.listeners = [];
6018         },
6019
6020         fire : function(){
6021             var ls = this.listeners, scope, len = ls.length;
6022             if(len > 0){
6023                 this.firing = true;
6024                 var args = Array.prototype.slice.call(arguments, 0);
6025                 for(var i = 0; i < len; i++){
6026                     var l = ls[i];
6027                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6028                         this.firing = false;
6029                         return false;
6030                     }
6031                 }
6032                 this.firing = false;
6033             }
6034             return true;
6035         }
6036     };
6037 })();/*
6038  * Based on:
6039  * Ext JS Library 1.1.1
6040  * Copyright(c) 2006-2007, Ext JS, LLC.
6041  *
6042  * Originally Released Under LGPL - original licence link has changed is not relivant.
6043  *
6044  * Fork - LGPL
6045  * <script type="text/javascript">
6046  */
6047
6048 /**
6049  * @class Roo.EventManager
6050  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6051  * several useful events directly.
6052  * See {@link Roo.EventObject} for more details on normalized event objects.
6053  * @singleton
6054  */
6055 Roo.EventManager = function(){
6056     var docReadyEvent, docReadyProcId, docReadyState = false;
6057     var resizeEvent, resizeTask, textEvent, textSize;
6058     var E = Roo.lib.Event;
6059     var D = Roo.lib.Dom;
6060
6061     
6062     
6063
6064     var fireDocReady = function(){
6065         if(!docReadyState){
6066             docReadyState = true;
6067             Roo.isReady = true;
6068             if(docReadyProcId){
6069                 clearInterval(docReadyProcId);
6070             }
6071             if(Roo.isGecko || Roo.isOpera) {
6072                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6073             }
6074             if(Roo.isIE){
6075                 var defer = document.getElementById("ie-deferred-loader");
6076                 if(defer){
6077                     defer.onreadystatechange = null;
6078                     defer.parentNode.removeChild(defer);
6079                 }
6080             }
6081             if(docReadyEvent){
6082                 docReadyEvent.fire();
6083                 docReadyEvent.clearListeners();
6084             }
6085         }
6086     };
6087     
6088     var initDocReady = function(){
6089         docReadyEvent = new Roo.util.Event();
6090         if(Roo.isGecko || Roo.isOpera) {
6091             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6092         }else if(Roo.isIE){
6093             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6094             var defer = document.getElementById("ie-deferred-loader");
6095             defer.onreadystatechange = function(){
6096                 if(this.readyState == "complete"){
6097                     fireDocReady();
6098                 }
6099             };
6100         }else if(Roo.isSafari){ 
6101             docReadyProcId = setInterval(function(){
6102                 var rs = document.readyState;
6103                 if(rs == "complete") {
6104                     fireDocReady();     
6105                  }
6106             }, 10);
6107         }
6108         // no matter what, make sure it fires on load
6109         E.on(window, "load", fireDocReady);
6110     };
6111
6112     var createBuffered = function(h, o){
6113         var task = new Roo.util.DelayedTask(h);
6114         return function(e){
6115             // create new event object impl so new events don't wipe out properties
6116             e = new Roo.EventObjectImpl(e);
6117             task.delay(o.buffer, h, null, [e]);
6118         };
6119     };
6120
6121     var createSingle = function(h, el, ename, fn){
6122         return function(e){
6123             Roo.EventManager.removeListener(el, ename, fn);
6124             h(e);
6125         };
6126     };
6127
6128     var createDelayed = function(h, o){
6129         return function(e){
6130             // create new event object impl so new events don't wipe out properties
6131             e = new Roo.EventObjectImpl(e);
6132             setTimeout(function(){
6133                 h(e);
6134             }, o.delay || 10);
6135         };
6136     };
6137     var transitionEndVal = false;
6138     
6139     var transitionEnd = function()
6140     {
6141         if (transitionEndVal) {
6142             return transitionEndVal;
6143         }
6144         var el = document.createElement('div');
6145
6146         var transEndEventNames = {
6147             WebkitTransition : 'webkitTransitionEnd',
6148             MozTransition    : 'transitionend',
6149             OTransition      : 'oTransitionEnd otransitionend',
6150             transition       : 'transitionend'
6151         };
6152     
6153         for (var name in transEndEventNames) {
6154             if (el.style[name] !== undefined) {
6155                 transitionEndVal = transEndEventNames[name];
6156                 return  transitionEndVal ;
6157             }
6158         }
6159     }
6160     
6161
6162     var listen = function(element, ename, opt, fn, scope){
6163         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6164         fn = fn || o.fn; scope = scope || o.scope;
6165         var el = Roo.getDom(element);
6166         
6167         
6168         if(!el){
6169             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6170         }
6171         
6172         if (ename == 'transitionend') {
6173             ename = transitionEnd();
6174         }
6175         var h = function(e){
6176             e = Roo.EventObject.setEvent(e);
6177             var t;
6178             if(o.delegate){
6179                 t = e.getTarget(o.delegate, el);
6180                 if(!t){
6181                     return;
6182                 }
6183             }else{
6184                 t = e.target;
6185             }
6186             if(o.stopEvent === true){
6187                 e.stopEvent();
6188             }
6189             if(o.preventDefault === true){
6190                e.preventDefault();
6191             }
6192             if(o.stopPropagation === true){
6193                 e.stopPropagation();
6194             }
6195
6196             if(o.normalized === false){
6197                 e = e.browserEvent;
6198             }
6199
6200             fn.call(scope || el, e, t, o);
6201         };
6202         if(o.delay){
6203             h = createDelayed(h, o);
6204         }
6205         if(o.single){
6206             h = createSingle(h, el, ename, fn);
6207         }
6208         if(o.buffer){
6209             h = createBuffered(h, o);
6210         }
6211         fn._handlers = fn._handlers || [];
6212         
6213         
6214         fn._handlers.push([Roo.id(el), ename, h]);
6215         
6216         
6217          
6218         E.on(el, ename, h);
6219         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6220             el.addEventListener("DOMMouseScroll", h, false);
6221             E.on(window, 'unload', function(){
6222                 el.removeEventListener("DOMMouseScroll", h, false);
6223             });
6224         }
6225         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6226             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6227         }
6228         return h;
6229     };
6230
6231     var stopListening = function(el, ename, fn){
6232         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6233         if(hds){
6234             for(var i = 0, len = hds.length; i < len; i++){
6235                 var h = hds[i];
6236                 if(h[0] == id && h[1] == ename){
6237                     hd = h[2];
6238                     hds.splice(i, 1);
6239                     break;
6240                 }
6241             }
6242         }
6243         E.un(el, ename, hd);
6244         el = Roo.getDom(el);
6245         if(ename == "mousewheel" && el.addEventListener){
6246             el.removeEventListener("DOMMouseScroll", hd, false);
6247         }
6248         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6249             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6250         }
6251     };
6252
6253     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6254     
6255     var pub = {
6256         
6257         
6258         /** 
6259          * Fix for doc tools
6260          * @scope Roo.EventManager
6261          */
6262         
6263         
6264         /** 
6265          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6266          * object with a Roo.EventObject
6267          * @param {Function} fn        The method the event invokes
6268          * @param {Object}   scope    An object that becomes the scope of the handler
6269          * @param {boolean}  override If true, the obj passed in becomes
6270          *                             the execution scope of the listener
6271          * @return {Function} The wrapped function
6272          * @deprecated
6273          */
6274         wrap : function(fn, scope, override){
6275             return function(e){
6276                 Roo.EventObject.setEvent(e);
6277                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6278             };
6279         },
6280         
6281         /**
6282      * Appends an event handler to an element (shorthand for addListener)
6283      * @param {String/HTMLElement}   element        The html element or id to assign the
6284      * @param {String}   eventName The type of event to listen for
6285      * @param {Function} handler The method the event invokes
6286      * @param {Object}   scope (optional) The scope in which to execute the handler
6287      * function. The handler function's "this" context.
6288      * @param {Object}   options (optional) An object containing handler configuration
6289      * properties. This may contain any of the following properties:<ul>
6290      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6291      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6292      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6293      * <li>preventDefault {Boolean} True to prevent the default action</li>
6294      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6295      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6296      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6297      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6298      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6299      * by the specified number of milliseconds. If the event fires again within that time, the original
6300      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6301      * </ul><br>
6302      * <p>
6303      * <b>Combining Options</b><br>
6304      * Using the options argument, it is possible to combine different types of listeners:<br>
6305      * <br>
6306      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6307      * Code:<pre><code>
6308 el.on('click', this.onClick, this, {
6309     single: true,
6310     delay: 100,
6311     stopEvent : true,
6312     forumId: 4
6313 });</code></pre>
6314      * <p>
6315      * <b>Attaching multiple handlers in 1 call</b><br>
6316       * The method also allows for a single argument to be passed which is a config object containing properties
6317      * which specify multiple handlers.
6318      * <p>
6319      * Code:<pre><code>
6320 el.on({
6321     'click' : {
6322         fn: this.onClick
6323         scope: this,
6324         delay: 100
6325     },
6326     'mouseover' : {
6327         fn: this.onMouseOver
6328         scope: this
6329     },
6330     'mouseout' : {
6331         fn: this.onMouseOut
6332         scope: this
6333     }
6334 });</code></pre>
6335      * <p>
6336      * Or a shorthand syntax:<br>
6337      * Code:<pre><code>
6338 el.on({
6339     'click' : this.onClick,
6340     'mouseover' : this.onMouseOver,
6341     'mouseout' : this.onMouseOut
6342     scope: this
6343 });</code></pre>
6344      */
6345         addListener : function(element, eventName, fn, scope, options){
6346             if(typeof eventName == "object"){
6347                 var o = eventName;
6348                 for(var e in o){
6349                     if(propRe.test(e)){
6350                         continue;
6351                     }
6352                     if(typeof o[e] == "function"){
6353                         // shared options
6354                         listen(element, e, o, o[e], o.scope);
6355                     }else{
6356                         // individual options
6357                         listen(element, e, o[e]);
6358                     }
6359                 }
6360                 return;
6361             }
6362             return listen(element, eventName, options, fn, scope);
6363         },
6364         
6365         /**
6366          * Removes an event handler
6367          *
6368          * @param {String/HTMLElement}   element        The id or html element to remove the 
6369          *                             event from
6370          * @param {String}   eventName     The type of event
6371          * @param {Function} fn
6372          * @return {Boolean} True if a listener was actually removed
6373          */
6374         removeListener : function(element, eventName, fn){
6375             return stopListening(element, eventName, fn);
6376         },
6377         
6378         /**
6379          * Fires when the document is ready (before onload and before images are loaded). Can be 
6380          * accessed shorthanded Roo.onReady().
6381          * @param {Function} fn        The method the event invokes
6382          * @param {Object}   scope    An  object that becomes the scope of the handler
6383          * @param {boolean}  options
6384          */
6385         onDocumentReady : function(fn, scope, options){
6386             if(docReadyState){ // if it already fired
6387                 docReadyEvent.addListener(fn, scope, options);
6388                 docReadyEvent.fire();
6389                 docReadyEvent.clearListeners();
6390                 return;
6391             }
6392             if(!docReadyEvent){
6393                 initDocReady();
6394             }
6395             docReadyEvent.addListener(fn, scope, options);
6396         },
6397         
6398         /**
6399          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6400          * @param {Function} fn        The method the event invokes
6401          * @param {Object}   scope    An object that becomes the scope of the handler
6402          * @param {boolean}  options
6403          */
6404         onWindowResize : function(fn, scope, options){
6405             if(!resizeEvent){
6406                 resizeEvent = new Roo.util.Event();
6407                 resizeTask = new Roo.util.DelayedTask(function(){
6408                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6409                 });
6410                 E.on(window, "resize", function(){
6411                     if(Roo.isIE){
6412                         resizeTask.delay(50);
6413                     }else{
6414                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6415                     }
6416                 });
6417             }
6418             resizeEvent.addListener(fn, scope, options);
6419         },
6420
6421         /**
6422          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6423          * @param {Function} fn        The method the event invokes
6424          * @param {Object}   scope    An object that becomes the scope of the handler
6425          * @param {boolean}  options
6426          */
6427         onTextResize : function(fn, scope, options){
6428             if(!textEvent){
6429                 textEvent = new Roo.util.Event();
6430                 var textEl = new Roo.Element(document.createElement('div'));
6431                 textEl.dom.className = 'x-text-resize';
6432                 textEl.dom.innerHTML = 'X';
6433                 textEl.appendTo(document.body);
6434                 textSize = textEl.dom.offsetHeight;
6435                 setInterval(function(){
6436                     if(textEl.dom.offsetHeight != textSize){
6437                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6438                     }
6439                 }, this.textResizeInterval);
6440             }
6441             textEvent.addListener(fn, scope, options);
6442         },
6443
6444         /**
6445          * Removes the passed window resize listener.
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    The scope of handler
6448          */
6449         removeResizeListener : function(fn, scope){
6450             if(resizeEvent){
6451                 resizeEvent.removeListener(fn, scope);
6452             }
6453         },
6454
6455         // private
6456         fireResize : function(){
6457             if(resizeEvent){
6458                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6459             }   
6460         },
6461         /**
6462          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6463          */
6464         ieDeferSrc : false,
6465         /**
6466          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6467          */
6468         textResizeInterval : 50
6469     };
6470     
6471     /**
6472      * Fix for doc tools
6473      * @scopeAlias pub=Roo.EventManager
6474      */
6475     
6476      /**
6477      * Appends an event handler to an element (shorthand for addListener)
6478      * @param {String/HTMLElement}   element        The html element or id to assign the
6479      * @param {String}   eventName The type of event to listen for
6480      * @param {Function} handler The method the event invokes
6481      * @param {Object}   scope (optional) The scope in which to execute the handler
6482      * function. The handler function's "this" context.
6483      * @param {Object}   options (optional) An object containing handler configuration
6484      * properties. This may contain any of the following properties:<ul>
6485      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6486      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6487      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6488      * <li>preventDefault {Boolean} True to prevent the default action</li>
6489      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6490      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6491      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6492      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6493      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6494      * by the specified number of milliseconds. If the event fires again within that time, the original
6495      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6496      * </ul><br>
6497      * <p>
6498      * <b>Combining Options</b><br>
6499      * Using the options argument, it is possible to combine different types of listeners:<br>
6500      * <br>
6501      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6502      * Code:<pre><code>
6503 el.on('click', this.onClick, this, {
6504     single: true,
6505     delay: 100,
6506     stopEvent : true,
6507     forumId: 4
6508 });</code></pre>
6509      * <p>
6510      * <b>Attaching multiple handlers in 1 call</b><br>
6511       * The method also allows for a single argument to be passed which is a config object containing properties
6512      * which specify multiple handlers.
6513      * <p>
6514      * Code:<pre><code>
6515 el.on({
6516     'click' : {
6517         fn: this.onClick
6518         scope: this,
6519         delay: 100
6520     },
6521     'mouseover' : {
6522         fn: this.onMouseOver
6523         scope: this
6524     },
6525     'mouseout' : {
6526         fn: this.onMouseOut
6527         scope: this
6528     }
6529 });</code></pre>
6530      * <p>
6531      * Or a shorthand syntax:<br>
6532      * Code:<pre><code>
6533 el.on({
6534     'click' : this.onClick,
6535     'mouseover' : this.onMouseOver,
6536     'mouseout' : this.onMouseOut
6537     scope: this
6538 });</code></pre>
6539      */
6540     pub.on = pub.addListener;
6541     pub.un = pub.removeListener;
6542
6543     pub.stoppedMouseDownEvent = new Roo.util.Event();
6544     return pub;
6545 }();
6546 /**
6547   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6548   * @param {Function} fn        The method the event invokes
6549   * @param {Object}   scope    An  object that becomes the scope of the handler
6550   * @param {boolean}  override If true, the obj passed in becomes
6551   *                             the execution scope of the listener
6552   * @member Roo
6553   * @method onReady
6554  */
6555 Roo.onReady = Roo.EventManager.onDocumentReady;
6556
6557 Roo.onReady(function(){
6558     var bd = Roo.get(document.body);
6559     if(!bd){ return; }
6560
6561     var cls = [
6562             Roo.isIE ? "roo-ie"
6563             : Roo.isGecko ? "roo-gecko"
6564             : Roo.isOpera ? "roo-opera"
6565             : Roo.isSafari ? "roo-safari" : ""];
6566
6567     if(Roo.isMac){
6568         cls.push("roo-mac");
6569     }
6570     if(Roo.isLinux){
6571         cls.push("roo-linux");
6572     }
6573     if(Roo.isBorderBox){
6574         cls.push('roo-border-box');
6575     }
6576     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6577         var p = bd.dom.parentNode;
6578         if(p){
6579             p.className += ' roo-strict';
6580         }
6581     }
6582     bd.addClass(cls.join(' '));
6583 });
6584
6585 /**
6586  * @class Roo.EventObject
6587  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6588  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6589  * Example:
6590  * <pre><code>
6591  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6592     e.preventDefault();
6593     var target = e.getTarget();
6594     ...
6595  }
6596  var myDiv = Roo.get("myDiv");
6597  myDiv.on("click", handleClick);
6598  //or
6599  Roo.EventManager.on("myDiv", 'click', handleClick);
6600  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6601  </code></pre>
6602  * @singleton
6603  */
6604 Roo.EventObject = function(){
6605     
6606     var E = Roo.lib.Event;
6607     
6608     // safari keypress events for special keys return bad keycodes
6609     var safariKeys = {
6610         63234 : 37, // left
6611         63235 : 39, // right
6612         63232 : 38, // up
6613         63233 : 40, // down
6614         63276 : 33, // page up
6615         63277 : 34, // page down
6616         63272 : 46, // delete
6617         63273 : 36, // home
6618         63275 : 35  // end
6619     };
6620
6621     // normalize button clicks
6622     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6623                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6624
6625     Roo.EventObjectImpl = function(e){
6626         if(e){
6627             this.setEvent(e.browserEvent || e);
6628         }
6629     };
6630     Roo.EventObjectImpl.prototype = {
6631         /**
6632          * Used to fix doc tools.
6633          * @scope Roo.EventObject.prototype
6634          */
6635             
6636
6637         
6638         
6639         /** The normal browser event */
6640         browserEvent : null,
6641         /** The button pressed in a mouse event */
6642         button : -1,
6643         /** True if the shift key was down during the event */
6644         shiftKey : false,
6645         /** True if the control key was down during the event */
6646         ctrlKey : false,
6647         /** True if the alt key was down during the event */
6648         altKey : false,
6649
6650         /** Key constant 
6651         * @type Number */
6652         BACKSPACE : 8,
6653         /** Key constant 
6654         * @type Number */
6655         TAB : 9,
6656         /** Key constant 
6657         * @type Number */
6658         RETURN : 13,
6659         /** Key constant 
6660         * @type Number */
6661         ENTER : 13,
6662         /** Key constant 
6663         * @type Number */
6664         SHIFT : 16,
6665         /** Key constant 
6666         * @type Number */
6667         CONTROL : 17,
6668         /** Key constant 
6669         * @type Number */
6670         ESC : 27,
6671         /** Key constant 
6672         * @type Number */
6673         SPACE : 32,
6674         /** Key constant 
6675         * @type Number */
6676         PAGEUP : 33,
6677         /** Key constant 
6678         * @type Number */
6679         PAGEDOWN : 34,
6680         /** Key constant 
6681         * @type Number */
6682         END : 35,
6683         /** Key constant 
6684         * @type Number */
6685         HOME : 36,
6686         /** Key constant 
6687         * @type Number */
6688         LEFT : 37,
6689         /** Key constant 
6690         * @type Number */
6691         UP : 38,
6692         /** Key constant 
6693         * @type Number */
6694         RIGHT : 39,
6695         /** Key constant 
6696         * @type Number */
6697         DOWN : 40,
6698         /** Key constant 
6699         * @type Number */
6700         DELETE : 46,
6701         /** Key constant 
6702         * @type Number */
6703         F5 : 116,
6704
6705            /** @private */
6706         setEvent : function(e){
6707             if(e == this || (e && e.browserEvent)){ // already wrapped
6708                 return e;
6709             }
6710             this.browserEvent = e;
6711             if(e){
6712                 // normalize buttons
6713                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6714                 if(e.type == 'click' && this.button == -1){
6715                     this.button = 0;
6716                 }
6717                 this.type = e.type;
6718                 this.shiftKey = e.shiftKey;
6719                 // mac metaKey behaves like ctrlKey
6720                 this.ctrlKey = e.ctrlKey || e.metaKey;
6721                 this.altKey = e.altKey;
6722                 // in getKey these will be normalized for the mac
6723                 this.keyCode = e.keyCode;
6724                 // keyup warnings on firefox.
6725                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6726                 // cache the target for the delayed and or buffered events
6727                 this.target = E.getTarget(e);
6728                 // same for XY
6729                 this.xy = E.getXY(e);
6730             }else{
6731                 this.button = -1;
6732                 this.shiftKey = false;
6733                 this.ctrlKey = false;
6734                 this.altKey = false;
6735                 this.keyCode = 0;
6736                 this.charCode =0;
6737                 this.target = null;
6738                 this.xy = [0, 0];
6739             }
6740             return this;
6741         },
6742
6743         /**
6744          * Stop the event (preventDefault and stopPropagation)
6745          */
6746         stopEvent : function(){
6747             if(this.browserEvent){
6748                 if(this.browserEvent.type == 'mousedown'){
6749                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6750                 }
6751                 E.stopEvent(this.browserEvent);
6752             }
6753         },
6754
6755         /**
6756          * Prevents the browsers default handling of the event.
6757          */
6758         preventDefault : function(){
6759             if(this.browserEvent){
6760                 E.preventDefault(this.browserEvent);
6761             }
6762         },
6763
6764         /** @private */
6765         isNavKeyPress : function(){
6766             var k = this.keyCode;
6767             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6768             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6769         },
6770
6771         isSpecialKey : function(){
6772             var k = this.keyCode;
6773             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6774             (k == 16) || (k == 17) ||
6775             (k >= 18 && k <= 20) ||
6776             (k >= 33 && k <= 35) ||
6777             (k >= 36 && k <= 39) ||
6778             (k >= 44 && k <= 45);
6779         },
6780         /**
6781          * Cancels bubbling of the event.
6782          */
6783         stopPropagation : function(){
6784             if(this.browserEvent){
6785                 if(this.type == 'mousedown'){
6786                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6787                 }
6788                 E.stopPropagation(this.browserEvent);
6789             }
6790         },
6791
6792         /**
6793          * Gets the key code for the event.
6794          * @return {Number}
6795          */
6796         getCharCode : function(){
6797             return this.charCode || this.keyCode;
6798         },
6799
6800         /**
6801          * Returns a normalized keyCode for the event.
6802          * @return {Number} The key code
6803          */
6804         getKey : function(){
6805             var k = this.keyCode || this.charCode;
6806             return Roo.isSafari ? (safariKeys[k] || k) : k;
6807         },
6808
6809         /**
6810          * Gets the x coordinate of the event.
6811          * @return {Number}
6812          */
6813         getPageX : function(){
6814             return this.xy[0];
6815         },
6816
6817         /**
6818          * Gets the y coordinate of the event.
6819          * @return {Number}
6820          */
6821         getPageY : function(){
6822             return this.xy[1];
6823         },
6824
6825         /**
6826          * Gets the time of the event.
6827          * @return {Number}
6828          */
6829         getTime : function(){
6830             if(this.browserEvent){
6831                 return E.getTime(this.browserEvent);
6832             }
6833             return null;
6834         },
6835
6836         /**
6837          * Gets the page coordinates of the event.
6838          * @return {Array} The xy values like [x, y]
6839          */
6840         getXY : function(){
6841             return this.xy;
6842         },
6843
6844         /**
6845          * Gets the target for the event.
6846          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6847          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6848                 search as a number or element (defaults to 10 || document.body)
6849          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6850          * @return {HTMLelement}
6851          */
6852         getTarget : function(selector, maxDepth, returnEl){
6853             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6854         },
6855         /**
6856          * Gets the related target.
6857          * @return {HTMLElement}
6858          */
6859         getRelatedTarget : function(){
6860             if(this.browserEvent){
6861                 return E.getRelatedTarget(this.browserEvent);
6862             }
6863             return null;
6864         },
6865
6866         /**
6867          * Normalizes mouse wheel delta across browsers
6868          * @return {Number} The delta
6869          */
6870         getWheelDelta : function(){
6871             var e = this.browserEvent;
6872             var delta = 0;
6873             if(e.wheelDelta){ /* IE/Opera. */
6874                 delta = e.wheelDelta/120;
6875             }else if(e.detail){ /* Mozilla case. */
6876                 delta = -e.detail/3;
6877             }
6878             return delta;
6879         },
6880
6881         /**
6882          * Returns true if the control, meta, shift or alt key was pressed during this event.
6883          * @return {Boolean}
6884          */
6885         hasModifier : function(){
6886             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6887         },
6888
6889         /**
6890          * Returns true if the target of this event equals el or is a child of el
6891          * @param {String/HTMLElement/Element} el
6892          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6893          * @return {Boolean}
6894          */
6895         within : function(el, related){
6896             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6897             return t && Roo.fly(el).contains(t);
6898         },
6899
6900         getPoint : function(){
6901             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6902         }
6903     };
6904
6905     return new Roo.EventObjectImpl();
6906 }();
6907             
6908     /*
6909  * Based on:
6910  * Ext JS Library 1.1.1
6911  * Copyright(c) 2006-2007, Ext JS, LLC.
6912  *
6913  * Originally Released Under LGPL - original licence link has changed is not relivant.
6914  *
6915  * Fork - LGPL
6916  * <script type="text/javascript">
6917  */
6918
6919  
6920 // was in Composite Element!??!?!
6921  
6922 (function(){
6923     var D = Roo.lib.Dom;
6924     var E = Roo.lib.Event;
6925     var A = Roo.lib.Anim;
6926
6927     // local style camelizing for speed
6928     var propCache = {};
6929     var camelRe = /(-[a-z])/gi;
6930     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6931     var view = document.defaultView;
6932
6933 /**
6934  * @class Roo.Element
6935  * Represents an Element in the DOM.<br><br>
6936  * Usage:<br>
6937 <pre><code>
6938 var el = Roo.get("my-div");
6939
6940 // or with getEl
6941 var el = getEl("my-div");
6942
6943 // or with a DOM element
6944 var el = Roo.get(myDivElement);
6945 </code></pre>
6946  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6947  * each call instead of constructing a new one.<br><br>
6948  * <b>Animations</b><br />
6949  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6950  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6951 <pre>
6952 Option    Default   Description
6953 --------- --------  ---------------------------------------------
6954 duration  .35       The duration of the animation in seconds
6955 easing    easeOut   The YUI easing method
6956 callback  none      A function to execute when the anim completes
6957 scope     this      The scope (this) of the callback function
6958 </pre>
6959 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6960 * manipulate the animation. Here's an example:
6961 <pre><code>
6962 var el = Roo.get("my-div");
6963
6964 // no animation
6965 el.setWidth(100);
6966
6967 // default animation
6968 el.setWidth(100, true);
6969
6970 // animation with some options set
6971 el.setWidth(100, {
6972     duration: 1,
6973     callback: this.foo,
6974     scope: this
6975 });
6976
6977 // using the "anim" property to get the Anim object
6978 var opt = {
6979     duration: 1,
6980     callback: this.foo,
6981     scope: this
6982 };
6983 el.setWidth(100, opt);
6984 ...
6985 if(opt.anim.isAnimated()){
6986     opt.anim.stop();
6987 }
6988 </code></pre>
6989 * <b> Composite (Collections of) Elements</b><br />
6990  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6991  * @constructor Create a new Element directly.
6992  * @param {String/HTMLElement} element
6993  * @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).
6994  */
6995     Roo.Element = function(element, forceNew){
6996         var dom = typeof element == "string" ?
6997                 document.getElementById(element) : element;
6998         if(!dom){ // invalid id/element
6999             return null;
7000         }
7001         var id = dom.id;
7002         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7003             return Roo.Element.cache[id];
7004         }
7005
7006         /**
7007          * The DOM element
7008          * @type HTMLElement
7009          */
7010         this.dom = dom;
7011
7012         /**
7013          * The DOM element ID
7014          * @type String
7015          */
7016         this.id = id || Roo.id(dom);
7017     };
7018
7019     var El = Roo.Element;
7020
7021     El.prototype = {
7022         /**
7023          * The element's default display mode  (defaults to "")
7024          * @type String
7025          */
7026         originalDisplay : "",
7027
7028         visibilityMode : 1,
7029         /**
7030          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7031          * @type String
7032          */
7033         defaultUnit : "px",
7034         /**
7035          * Sets the element's visibility mode. When setVisible() is called it
7036          * will use this to determine whether to set the visibility or the display property.
7037          * @param visMode Element.VISIBILITY or Element.DISPLAY
7038          * @return {Roo.Element} this
7039          */
7040         setVisibilityMode : function(visMode){
7041             this.visibilityMode = visMode;
7042             return this;
7043         },
7044         /**
7045          * Convenience method for setVisibilityMode(Element.DISPLAY)
7046          * @param {String} display (optional) What to set display to when visible
7047          * @return {Roo.Element} this
7048          */
7049         enableDisplayMode : function(display){
7050             this.setVisibilityMode(El.DISPLAY);
7051             if(typeof display != "undefined") this.originalDisplay = display;
7052             return this;
7053         },
7054
7055         /**
7056          * 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)
7057          * @param {String} selector The simple selector to test
7058          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7059                 search as a number or element (defaults to 10 || document.body)
7060          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7061          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7062          */
7063         findParent : function(simpleSelector, maxDepth, returnEl){
7064             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7065             maxDepth = maxDepth || 50;
7066             if(typeof maxDepth != "number"){
7067                 stopEl = Roo.getDom(maxDepth);
7068                 maxDepth = 10;
7069             }
7070             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7071                 if(dq.is(p, simpleSelector)){
7072                     return returnEl ? Roo.get(p) : p;
7073                 }
7074                 depth++;
7075                 p = p.parentNode;
7076             }
7077             return null;
7078         },
7079
7080
7081         /**
7082          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7083          * @param {String} selector The simple selector to test
7084          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7085                 search as a number or element (defaults to 10 || document.body)
7086          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7087          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7088          */
7089         findParentNode : function(simpleSelector, maxDepth, returnEl){
7090             var p = Roo.fly(this.dom.parentNode, '_internal');
7091             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7092         },
7093
7094         /**
7095          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7096          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7097          * @param {String} selector The simple selector to test
7098          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7099                 search as a number or element (defaults to 10 || document.body)
7100          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7101          */
7102         up : function(simpleSelector, maxDepth){
7103             return this.findParentNode(simpleSelector, maxDepth, true);
7104         },
7105
7106
7107
7108         /**
7109          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7110          * @param {String} selector The simple selector to test
7111          * @return {Boolean} True if this element matches the selector, else false
7112          */
7113         is : function(simpleSelector){
7114             return Roo.DomQuery.is(this.dom, simpleSelector);
7115         },
7116
7117         /**
7118          * Perform animation on this element.
7119          * @param {Object} args The YUI animation control args
7120          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7121          * @param {Function} onComplete (optional) Function to call when animation completes
7122          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7123          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7124          * @return {Roo.Element} this
7125          */
7126         animate : function(args, duration, onComplete, easing, animType){
7127             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7128             return this;
7129         },
7130
7131         /*
7132          * @private Internal animation call
7133          */
7134         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7135             animType = animType || 'run';
7136             opt = opt || {};
7137             var anim = Roo.lib.Anim[animType](
7138                 this.dom, args,
7139                 (opt.duration || defaultDur) || .35,
7140                 (opt.easing || defaultEase) || 'easeOut',
7141                 function(){
7142                     Roo.callback(cb, this);
7143                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7144                 },
7145                 this
7146             );
7147             opt.anim = anim;
7148             return anim;
7149         },
7150
7151         // private legacy anim prep
7152         preanim : function(a, i){
7153             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7154         },
7155
7156         /**
7157          * Removes worthless text nodes
7158          * @param {Boolean} forceReclean (optional) By default the element
7159          * keeps track if it has been cleaned already so
7160          * you can call this over and over. However, if you update the element and
7161          * need to force a reclean, you can pass true.
7162          */
7163         clean : function(forceReclean){
7164             if(this.isCleaned && forceReclean !== true){
7165                 return this;
7166             }
7167             var ns = /\S/;
7168             var d = this.dom, n = d.firstChild, ni = -1;
7169             while(n){
7170                 var nx = n.nextSibling;
7171                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7172                     d.removeChild(n);
7173                 }else{
7174                     n.nodeIndex = ++ni;
7175                 }
7176                 n = nx;
7177             }
7178             this.isCleaned = true;
7179             return this;
7180         },
7181
7182         // private
7183         calcOffsetsTo : function(el){
7184             el = Roo.get(el);
7185             var d = el.dom;
7186             var restorePos = false;
7187             if(el.getStyle('position') == 'static'){
7188                 el.position('relative');
7189                 restorePos = true;
7190             }
7191             var x = 0, y =0;
7192             var op = this.dom;
7193             while(op && op != d && op.tagName != 'HTML'){
7194                 x+= op.offsetLeft;
7195                 y+= op.offsetTop;
7196                 op = op.offsetParent;
7197             }
7198             if(restorePos){
7199                 el.position('static');
7200             }
7201             return [x, y];
7202         },
7203
7204         /**
7205          * Scrolls this element into view within the passed container.
7206          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7207          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7208          * @return {Roo.Element} this
7209          */
7210         scrollIntoView : function(container, hscroll){
7211             var c = Roo.getDom(container) || document.body;
7212             var el = this.dom;
7213
7214             var o = this.calcOffsetsTo(c),
7215                 l = o[0],
7216                 t = o[1],
7217                 b = t+el.offsetHeight,
7218                 r = l+el.offsetWidth;
7219
7220             var ch = c.clientHeight;
7221             var ct = parseInt(c.scrollTop, 10);
7222             var cl = parseInt(c.scrollLeft, 10);
7223             var cb = ct + ch;
7224             var cr = cl + c.clientWidth;
7225
7226             if(t < ct){
7227                 c.scrollTop = t;
7228             }else if(b > cb){
7229                 c.scrollTop = b-ch;
7230             }
7231
7232             if(hscroll !== false){
7233                 if(l < cl){
7234                     c.scrollLeft = l;
7235                 }else if(r > cr){
7236                     c.scrollLeft = r-c.clientWidth;
7237                 }
7238             }
7239             return this;
7240         },
7241
7242         // private
7243         scrollChildIntoView : function(child, hscroll){
7244             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7245         },
7246
7247         /**
7248          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7249          * the new height may not be available immediately.
7250          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7251          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7252          * @param {Function} onComplete (optional) Function to call when animation completes
7253          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7254          * @return {Roo.Element} this
7255          */
7256         autoHeight : function(animate, duration, onComplete, easing){
7257             var oldHeight = this.getHeight();
7258             this.clip();
7259             this.setHeight(1); // force clipping
7260             setTimeout(function(){
7261                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7262                 if(!animate){
7263                     this.setHeight(height);
7264                     this.unclip();
7265                     if(typeof onComplete == "function"){
7266                         onComplete();
7267                     }
7268                 }else{
7269                     this.setHeight(oldHeight); // restore original height
7270                     this.setHeight(height, animate, duration, function(){
7271                         this.unclip();
7272                         if(typeof onComplete == "function") onComplete();
7273                     }.createDelegate(this), easing);
7274                 }
7275             }.createDelegate(this), 0);
7276             return this;
7277         },
7278
7279         /**
7280          * Returns true if this element is an ancestor of the passed element
7281          * @param {HTMLElement/String} el The element to check
7282          * @return {Boolean} True if this element is an ancestor of el, else false
7283          */
7284         contains : function(el){
7285             if(!el){return false;}
7286             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7287         },
7288
7289         /**
7290          * Checks whether the element is currently visible using both visibility and display properties.
7291          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7292          * @return {Boolean} True if the element is currently visible, else false
7293          */
7294         isVisible : function(deep) {
7295             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7296             if(deep !== true || !vis){
7297                 return vis;
7298             }
7299             var p = this.dom.parentNode;
7300             while(p && p.tagName.toLowerCase() != "body"){
7301                 if(!Roo.fly(p, '_isVisible').isVisible()){
7302                     return false;
7303                 }
7304                 p = p.parentNode;
7305             }
7306             return true;
7307         },
7308
7309         /**
7310          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7311          * @param {String} selector The CSS selector
7312          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7313          * @return {CompositeElement/CompositeElementLite} The composite element
7314          */
7315         select : function(selector, unique){
7316             return El.select(selector, unique, this.dom);
7317         },
7318
7319         /**
7320          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7321          * @param {String} selector The CSS selector
7322          * @return {Array} An array of the matched nodes
7323          */
7324         query : function(selector, unique){
7325             return Roo.DomQuery.select(selector, this.dom);
7326         },
7327
7328         /**
7329          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         child : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7341          * @param {String} selector The CSS selector
7342          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7343          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7344          */
7345         down : function(selector, returnDom){
7346             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7347             return returnDom ? n : Roo.get(n);
7348         },
7349
7350         /**
7351          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7352          * @param {String} group The group the DD object is member of
7353          * @param {Object} config The DD config object
7354          * @param {Object} overrides An object containing methods to override/implement on the DD object
7355          * @return {Roo.dd.DD} The DD object
7356          */
7357         initDD : function(group, config, overrides){
7358             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7359             return Roo.apply(dd, overrides);
7360         },
7361
7362         /**
7363          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7364          * @param {String} group The group the DDProxy object is member of
7365          * @param {Object} config The DDProxy config object
7366          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7367          * @return {Roo.dd.DDProxy} The DDProxy object
7368          */
7369         initDDProxy : function(group, config, overrides){
7370             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7371             return Roo.apply(dd, overrides);
7372         },
7373
7374         /**
7375          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7376          * @param {String} group The group the DDTarget object is member of
7377          * @param {Object} config The DDTarget config object
7378          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7379          * @return {Roo.dd.DDTarget} The DDTarget object
7380          */
7381         initDDTarget : function(group, config, overrides){
7382             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7383             return Roo.apply(dd, overrides);
7384         },
7385
7386         /**
7387          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7388          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7389          * @param {Boolean} visible Whether the element is visible
7390          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7391          * @return {Roo.Element} this
7392          */
7393          setVisible : function(visible, animate){
7394             if(!animate || !A){
7395                 if(this.visibilityMode == El.DISPLAY){
7396                     this.setDisplayed(visible);
7397                 }else{
7398                     this.fixDisplay();
7399                     this.dom.style.visibility = visible ? "visible" : "hidden";
7400                 }
7401             }else{
7402                 // closure for composites
7403                 var dom = this.dom;
7404                 var visMode = this.visibilityMode;
7405                 if(visible){
7406                     this.setOpacity(.01);
7407                     this.setVisible(true);
7408                 }
7409                 this.anim({opacity: { to: (visible?1:0) }},
7410                       this.preanim(arguments, 1),
7411                       null, .35, 'easeIn', function(){
7412                          if(!visible){
7413                              if(visMode == El.DISPLAY){
7414                                  dom.style.display = "none";
7415                              }else{
7416                                  dom.style.visibility = "hidden";
7417                              }
7418                              Roo.get(dom).setOpacity(1);
7419                          }
7420                      });
7421             }
7422             return this;
7423         },
7424
7425         /**
7426          * Returns true if display is not "none"
7427          * @return {Boolean}
7428          */
7429         isDisplayed : function() {
7430             return this.getStyle("display") != "none";
7431         },
7432
7433         /**
7434          * Toggles the element's visibility or display, depending on visibility mode.
7435          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7436          * @return {Roo.Element} this
7437          */
7438         toggle : function(animate){
7439             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7440             return this;
7441         },
7442
7443         /**
7444          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7445          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7446          * @return {Roo.Element} this
7447          */
7448         setDisplayed : function(value) {
7449             if(typeof value == "boolean"){
7450                value = value ? this.originalDisplay : "none";
7451             }
7452             this.setStyle("display", value);
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to focus the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         focus : function() {
7461             try{
7462                 this.dom.focus();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Tries to blur the element. Any exceptions are caught and ignored.
7469          * @return {Roo.Element} this
7470          */
7471         blur : function() {
7472             try{
7473                 this.dom.blur();
7474             }catch(e){}
7475             return this;
7476         },
7477
7478         /**
7479          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7480          * @param {String/Array} className The CSS class to add, or an array of classes
7481          * @return {Roo.Element} this
7482          */
7483         addClass : function(className){
7484             if(className instanceof Array){
7485                 for(var i = 0, len = className.length; i < len; i++) {
7486                     this.addClass(className[i]);
7487                 }
7488             }else{
7489                 if(className && !this.hasClass(className)){
7490                     this.dom.className = this.dom.className + " " + className;
7491                 }
7492             }
7493             return this;
7494         },
7495
7496         /**
7497          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7498          * @param {String/Array} className The CSS class to add, or an array of classes
7499          * @return {Roo.Element} this
7500          */
7501         radioClass : function(className){
7502             var siblings = this.dom.parentNode.childNodes;
7503             for(var i = 0; i < siblings.length; i++) {
7504                 var s = siblings[i];
7505                 if(s.nodeType == 1){
7506                     Roo.get(s).removeClass(className);
7507                 }
7508             }
7509             this.addClass(className);
7510             return this;
7511         },
7512
7513         /**
7514          * Removes one or more CSS classes from the element.
7515          * @param {String/Array} className The CSS class to remove, or an array of classes
7516          * @return {Roo.Element} this
7517          */
7518         removeClass : function(className){
7519             if(!className || !this.dom.className){
7520                 return this;
7521             }
7522             if(className instanceof Array){
7523                 for(var i = 0, len = className.length; i < len; i++) {
7524                     this.removeClass(className[i]);
7525                 }
7526             }else{
7527                 if(this.hasClass(className)){
7528                     var re = this.classReCache[className];
7529                     if (!re) {
7530                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7531                        this.classReCache[className] = re;
7532                     }
7533                     this.dom.className =
7534                         this.dom.className.replace(re, " ");
7535                 }
7536             }
7537             return this;
7538         },
7539
7540         // private
7541         classReCache: {},
7542
7543         /**
7544          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7545          * @param {String} className The CSS class to toggle
7546          * @return {Roo.Element} this
7547          */
7548         toggleClass : function(className){
7549             if(this.hasClass(className)){
7550                 this.removeClass(className);
7551             }else{
7552                 this.addClass(className);
7553             }
7554             return this;
7555         },
7556
7557         /**
7558          * Checks if the specified CSS class exists on this element's DOM node.
7559          * @param {String} className The CSS class to check for
7560          * @return {Boolean} True if the class exists, else false
7561          */
7562         hasClass : function(className){
7563             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7564         },
7565
7566         /**
7567          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7568          * @param {String} oldClassName The CSS class to replace
7569          * @param {String} newClassName The replacement CSS class
7570          * @return {Roo.Element} this
7571          */
7572         replaceClass : function(oldClassName, newClassName){
7573             this.removeClass(oldClassName);
7574             this.addClass(newClassName);
7575             return this;
7576         },
7577
7578         /**
7579          * Returns an object with properties matching the styles requested.
7580          * For example, el.getStyles('color', 'font-size', 'width') might return
7581          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7582          * @param {String} style1 A style name
7583          * @param {String} style2 A style name
7584          * @param {String} etc.
7585          * @return {Object} The style object
7586          */
7587         getStyles : function(){
7588             var a = arguments, len = a.length, r = {};
7589             for(var i = 0; i < len; i++){
7590                 r[a[i]] = this.getStyle(a[i]);
7591             }
7592             return r;
7593         },
7594
7595         /**
7596          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7597          * @param {String} property The style property whose value is returned.
7598          * @return {String} The current value of the style property for this element.
7599          */
7600         getStyle : function(){
7601             return view && view.getComputedStyle ?
7602                 function(prop){
7603                     var el = this.dom, v, cs, camel;
7604                     if(prop == 'float'){
7605                         prop = "cssFloat";
7606                     }
7607                     if(el.style && (v = el.style[prop])){
7608                         return v;
7609                     }
7610                     if(cs = view.getComputedStyle(el, "")){
7611                         if(!(camel = propCache[prop])){
7612                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7613                         }
7614                         return cs[camel];
7615                     }
7616                     return null;
7617                 } :
7618                 function(prop){
7619                     var el = this.dom, v, cs, camel;
7620                     if(prop == 'opacity'){
7621                         if(typeof el.style.filter == 'string'){
7622                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7623                             if(m){
7624                                 var fv = parseFloat(m[1]);
7625                                 if(!isNaN(fv)){
7626                                     return fv ? fv / 100 : 0;
7627                                 }
7628                             }
7629                         }
7630                         return 1;
7631                     }else if(prop == 'float'){
7632                         prop = "styleFloat";
7633                     }
7634                     if(!(camel = propCache[prop])){
7635                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7636                     }
7637                     if(v = el.style[camel]){
7638                         return v;
7639                     }
7640                     if(cs = el.currentStyle){
7641                         return cs[camel];
7642                     }
7643                     return null;
7644                 };
7645         }(),
7646
7647         /**
7648          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7649          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7650          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7651          * @return {Roo.Element} this
7652          */
7653         setStyle : function(prop, value){
7654             if(typeof prop == "string"){
7655                 
7656                 if (prop == 'float') {
7657                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7658                     return this;
7659                 }
7660                 
7661                 var camel;
7662                 if(!(camel = propCache[prop])){
7663                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7664                 }
7665                 
7666                 if(camel == 'opacity') {
7667                     this.setOpacity(value);
7668                 }else{
7669                     this.dom.style[camel] = value;
7670                 }
7671             }else{
7672                 for(var style in prop){
7673                     if(typeof prop[style] != "function"){
7674                        this.setStyle(style, prop[style]);
7675                     }
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         /**
7682          * More flexible version of {@link #setStyle} for setting style properties.
7683          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7684          * a function which returns such a specification.
7685          * @return {Roo.Element} this
7686          */
7687         applyStyles : function(style){
7688             Roo.DomHelper.applyStyles(this.dom, style);
7689             return this;
7690         },
7691
7692         /**
7693           * 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).
7694           * @return {Number} The X position of the element
7695           */
7696         getX : function(){
7697             return D.getX(this.dom);
7698         },
7699
7700         /**
7701           * 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).
7702           * @return {Number} The Y position of the element
7703           */
7704         getY : function(){
7705             return D.getY(this.dom);
7706         },
7707
7708         /**
7709           * 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).
7710           * @return {Array} The XY position of the element
7711           */
7712         getXY : function(){
7713             return D.getXY(this.dom);
7714         },
7715
7716         /**
7717          * 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).
7718          * @param {Number} The X position of the element
7719          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7720          * @return {Roo.Element} this
7721          */
7722         setX : function(x, animate){
7723             if(!animate || !A){
7724                 D.setX(this.dom, x);
7725             }else{
7726                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7727             }
7728             return this;
7729         },
7730
7731         /**
7732          * 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).
7733          * @param {Number} The Y position of the element
7734          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7735          * @return {Roo.Element} this
7736          */
7737         setY : function(y, animate){
7738             if(!animate || !A){
7739                 D.setY(this.dom, y);
7740             }else{
7741                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7748          * @param {String} left The left CSS property value
7749          * @return {Roo.Element} this
7750          */
7751         setLeft : function(left){
7752             this.setStyle("left", this.addUnits(left));
7753             return this;
7754         },
7755
7756         /**
7757          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7758          * @param {String} top The top CSS property value
7759          * @return {Roo.Element} this
7760          */
7761         setTop : function(top){
7762             this.setStyle("top", this.addUnits(top));
7763             return this;
7764         },
7765
7766         /**
7767          * Sets the element's CSS right style.
7768          * @param {String} right The right CSS property value
7769          * @return {Roo.Element} this
7770          */
7771         setRight : function(right){
7772             this.setStyle("right", this.addUnits(right));
7773             return this;
7774         },
7775
7776         /**
7777          * Sets the element's CSS bottom style.
7778          * @param {String} bottom The bottom CSS property value
7779          * @return {Roo.Element} this
7780          */
7781         setBottom : function(bottom){
7782             this.setStyle("bottom", this.addUnits(bottom));
7783             return this;
7784         },
7785
7786         /**
7787          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7788          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7789          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7790          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7791          * @return {Roo.Element} this
7792          */
7793         setXY : function(pos, animate){
7794             if(!animate || !A){
7795                 D.setXY(this.dom, pos);
7796             }else{
7797                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7798             }
7799             return this;
7800         },
7801
7802         /**
7803          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7804          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7805          * @param {Number} x X value for new position (coordinates are page-based)
7806          * @param {Number} y Y value for new position (coordinates are page-based)
7807          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7808          * @return {Roo.Element} this
7809          */
7810         setLocation : function(x, y, animate){
7811             this.setXY([x, y], this.preanim(arguments, 2));
7812             return this;
7813         },
7814
7815         /**
7816          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7817          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7818          * @param {Number} x X value for new position (coordinates are page-based)
7819          * @param {Number} y Y value for new position (coordinates are page-based)
7820          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7821          * @return {Roo.Element} this
7822          */
7823         moveTo : function(x, y, animate){
7824             this.setXY([x, y], this.preanim(arguments, 2));
7825             return this;
7826         },
7827
7828         /**
7829          * Returns the region of the given element.
7830          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7831          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7832          */
7833         getRegion : function(){
7834             return D.getRegion(this.dom);
7835         },
7836
7837         /**
7838          * Returns the offset height of the element
7839          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7840          * @return {Number} The element's height
7841          */
7842         getHeight : function(contentHeight){
7843             var h = this.dom.offsetHeight || 0;
7844             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7845         },
7846
7847         /**
7848          * Returns the offset width of the element
7849          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7850          * @return {Number} The element's width
7851          */
7852         getWidth : function(contentWidth){
7853             var w = this.dom.offsetWidth || 0;
7854             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7855         },
7856
7857         /**
7858          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7859          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7860          * if a height has not been set using CSS.
7861          * @return {Number}
7862          */
7863         getComputedHeight : function(){
7864             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7865             if(!h){
7866                 h = parseInt(this.getStyle('height'), 10) || 0;
7867                 if(!this.isBorderBox()){
7868                     h += this.getFrameWidth('tb');
7869                 }
7870             }
7871             return h;
7872         },
7873
7874         /**
7875          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7876          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7877          * if a width has not been set using CSS.
7878          * @return {Number}
7879          */
7880         getComputedWidth : function(){
7881             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7882             if(!w){
7883                 w = parseInt(this.getStyle('width'), 10) || 0;
7884                 if(!this.isBorderBox()){
7885                     w += this.getFrameWidth('lr');
7886                 }
7887             }
7888             return w;
7889         },
7890
7891         /**
7892          * Returns the size of the element.
7893          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7894          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7895          */
7896         getSize : function(contentSize){
7897             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7898         },
7899
7900         /**
7901          * Returns the width and height of the viewport.
7902          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7903          */
7904         getViewSize : function(){
7905             var d = this.dom, doc = document, aw = 0, ah = 0;
7906             if(d == doc || d == doc.body){
7907                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7908             }else{
7909                 return {
7910                     width : d.clientWidth,
7911                     height: d.clientHeight
7912                 };
7913             }
7914         },
7915
7916         /**
7917          * Returns the value of the "value" attribute
7918          * @param {Boolean} asNumber true to parse the value as a number
7919          * @return {String/Number}
7920          */
7921         getValue : function(asNumber){
7922             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7923         },
7924
7925         // private
7926         adjustWidth : function(width){
7927             if(typeof width == "number"){
7928                 if(this.autoBoxAdjust && !this.isBorderBox()){
7929                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7930                 }
7931                 if(width < 0){
7932                     width = 0;
7933                 }
7934             }
7935             return width;
7936         },
7937
7938         // private
7939         adjustHeight : function(height){
7940             if(typeof height == "number"){
7941                if(this.autoBoxAdjust && !this.isBorderBox()){
7942                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7943                }
7944                if(height < 0){
7945                    height = 0;
7946                }
7947             }
7948             return height;
7949         },
7950
7951         /**
7952          * Set the width of the element
7953          * @param {Number} width The new width
7954          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7955          * @return {Roo.Element} this
7956          */
7957         setWidth : function(width, animate){
7958             width = this.adjustWidth(width);
7959             if(!animate || !A){
7960                 this.dom.style.width = this.addUnits(width);
7961             }else{
7962                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7963             }
7964             return this;
7965         },
7966
7967         /**
7968          * Set the height of the element
7969          * @param {Number} height The new height
7970          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973          setHeight : function(height, animate){
7974             height = this.adjustHeight(height);
7975             if(!animate || !A){
7976                 this.dom.style.height = this.addUnits(height);
7977             }else{
7978                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7979             }
7980             return this;
7981         },
7982
7983         /**
7984          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7985          * @param {Number} width The new width
7986          * @param {Number} height The new height
7987          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7988          * @return {Roo.Element} this
7989          */
7990          setSize : function(width, height, animate){
7991             if(typeof width == "object"){ // in case of object from getSize()
7992                 height = width.height; width = width.width;
7993             }
7994             width = this.adjustWidth(width); height = this.adjustHeight(height);
7995             if(!animate || !A){
7996                 this.dom.style.width = this.addUnits(width);
7997                 this.dom.style.height = this.addUnits(height);
7998             }else{
7999                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8000             }
8001             return this;
8002         },
8003
8004         /**
8005          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8006          * @param {Number} x X value for new position (coordinates are page-based)
8007          * @param {Number} y Y value for new position (coordinates are page-based)
8008          * @param {Number} width The new width
8009          * @param {Number} height The new height
8010          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8011          * @return {Roo.Element} this
8012          */
8013         setBounds : function(x, y, width, height, animate){
8014             if(!animate || !A){
8015                 this.setSize(width, height);
8016                 this.setLocation(x, y);
8017             }else{
8018                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8019                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8020                               this.preanim(arguments, 4), 'motion');
8021             }
8022             return this;
8023         },
8024
8025         /**
8026          * 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.
8027          * @param {Roo.lib.Region} region The region to fill
8028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8029          * @return {Roo.Element} this
8030          */
8031         setRegion : function(region, animate){
8032             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8033             return this;
8034         },
8035
8036         /**
8037          * Appends an event handler
8038          *
8039          * @param {String}   eventName     The type of event to append
8040          * @param {Function} fn        The method the event invokes
8041          * @param {Object} scope       (optional) The scope (this object) of the fn
8042          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8043          */
8044         addListener : function(eventName, fn, scope, options){
8045             if (this.dom) {
8046                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8047             }
8048         },
8049
8050         /**
8051          * Removes an event handler from this element
8052          * @param {String} eventName the type of event to remove
8053          * @param {Function} fn the method the event invokes
8054          * @return {Roo.Element} this
8055          */
8056         removeListener : function(eventName, fn){
8057             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8058             return this;
8059         },
8060
8061         /**
8062          * Removes all previous added listeners from this element
8063          * @return {Roo.Element} this
8064          */
8065         removeAllListeners : function(){
8066             E.purgeElement(this.dom);
8067             return this;
8068         },
8069
8070         relayEvent : function(eventName, observable){
8071             this.on(eventName, function(e){
8072                 observable.fireEvent(eventName, e);
8073             });
8074         },
8075
8076         /**
8077          * Set the opacity of the element
8078          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8079          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8080          * @return {Roo.Element} this
8081          */
8082          setOpacity : function(opacity, animate){
8083             if(!animate || !A){
8084                 var s = this.dom.style;
8085                 if(Roo.isIE){
8086                     s.zoom = 1;
8087                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8088                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8089                 }else{
8090                     s.opacity = opacity;
8091                 }
8092             }else{
8093                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8094             }
8095             return this;
8096         },
8097
8098         /**
8099          * Gets the left X coordinate
8100          * @param {Boolean} local True to get the local css position instead of page coordinate
8101          * @return {Number}
8102          */
8103         getLeft : function(local){
8104             if(!local){
8105                 return this.getX();
8106             }else{
8107                 return parseInt(this.getStyle("left"), 10) || 0;
8108             }
8109         },
8110
8111         /**
8112          * Gets the right X coordinate of the element (element X position + element width)
8113          * @param {Boolean} local True to get the local css position instead of page coordinate
8114          * @return {Number}
8115          */
8116         getRight : function(local){
8117             if(!local){
8118                 return this.getX() + this.getWidth();
8119             }else{
8120                 return (this.getLeft(true) + this.getWidth()) || 0;
8121             }
8122         },
8123
8124         /**
8125          * Gets the top Y coordinate
8126          * @param {Boolean} local True to get the local css position instead of page coordinate
8127          * @return {Number}
8128          */
8129         getTop : function(local) {
8130             if(!local){
8131                 return this.getY();
8132             }else{
8133                 return parseInt(this.getStyle("top"), 10) || 0;
8134             }
8135         },
8136
8137         /**
8138          * Gets the bottom Y coordinate of the element (element Y position + element height)
8139          * @param {Boolean} local True to get the local css position instead of page coordinate
8140          * @return {Number}
8141          */
8142         getBottom : function(local){
8143             if(!local){
8144                 return this.getY() + this.getHeight();
8145             }else{
8146                 return (this.getTop(true) + this.getHeight()) || 0;
8147             }
8148         },
8149
8150         /**
8151         * Initializes positioning on this element. If a desired position is not passed, it will make the
8152         * the element positioned relative IF it is not already positioned.
8153         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8154         * @param {Number} zIndex (optional) The zIndex to apply
8155         * @param {Number} x (optional) Set the page X position
8156         * @param {Number} y (optional) Set the page Y position
8157         */
8158         position : function(pos, zIndex, x, y){
8159             if(!pos){
8160                if(this.getStyle('position') == 'static'){
8161                    this.setStyle('position', 'relative');
8162                }
8163             }else{
8164                 this.setStyle("position", pos);
8165             }
8166             if(zIndex){
8167                 this.setStyle("z-index", zIndex);
8168             }
8169             if(x !== undefined && y !== undefined){
8170                 this.setXY([x, y]);
8171             }else if(x !== undefined){
8172                 this.setX(x);
8173             }else if(y !== undefined){
8174                 this.setY(y);
8175             }
8176         },
8177
8178         /**
8179         * Clear positioning back to the default when the document was loaded
8180         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8181         * @return {Roo.Element} this
8182          */
8183         clearPositioning : function(value){
8184             value = value ||'';
8185             this.setStyle({
8186                 "left": value,
8187                 "right": value,
8188                 "top": value,
8189                 "bottom": value,
8190                 "z-index": "",
8191                 "position" : "static"
8192             });
8193             return this;
8194         },
8195
8196         /**
8197         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8198         * snapshot before performing an update and then restoring the element.
8199         * @return {Object}
8200         */
8201         getPositioning : function(){
8202             var l = this.getStyle("left");
8203             var t = this.getStyle("top");
8204             return {
8205                 "position" : this.getStyle("position"),
8206                 "left" : l,
8207                 "right" : l ? "" : this.getStyle("right"),
8208                 "top" : t,
8209                 "bottom" : t ? "" : this.getStyle("bottom"),
8210                 "z-index" : this.getStyle("z-index")
8211             };
8212         },
8213
8214         /**
8215          * Gets the width of the border(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 border (l)eft width + the border (r)ight width.
8218          * @return {Number} The width of the sides passed added together
8219          */
8220         getBorderWidth : function(side){
8221             return this.addStyles(side, El.borders);
8222         },
8223
8224         /**
8225          * Gets the width of the padding(s) for the specified side(s)
8226          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8227          * passing lr would get the padding (l)eft + the padding (r)ight.
8228          * @return {Number} The padding of the sides passed added together
8229          */
8230         getPadding : function(side){
8231             return this.addStyles(side, El.paddings);
8232         },
8233
8234         /**
8235         * Set positioning with an object returned by getPositioning().
8236         * @param {Object} posCfg
8237         * @return {Roo.Element} this
8238          */
8239         setPositioning : function(pc){
8240             this.applyStyles(pc);
8241             if(pc.right == "auto"){
8242                 this.dom.style.right = "";
8243             }
8244             if(pc.bottom == "auto"){
8245                 this.dom.style.bottom = "";
8246             }
8247             return this;
8248         },
8249
8250         // private
8251         fixDisplay : function(){
8252             if(this.getStyle("display") == "none"){
8253                 this.setStyle("visibility", "hidden");
8254                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8255                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8256                     this.setStyle("display", "block");
8257                 }
8258             }
8259         },
8260
8261         /**
8262          * Quick set left and top adding default units
8263          * @param {String} left The left CSS property value
8264          * @param {String} top The top CSS property value
8265          * @return {Roo.Element} this
8266          */
8267          setLeftTop : function(left, top){
8268             this.dom.style.left = this.addUnits(left);
8269             this.dom.style.top = this.addUnits(top);
8270             return this;
8271         },
8272
8273         /**
8274          * Move this element relative to its current position.
8275          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8276          * @param {Number} distance How far to move the element in pixels
8277          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8278          * @return {Roo.Element} this
8279          */
8280          move : function(direction, distance, animate){
8281             var xy = this.getXY();
8282             direction = direction.toLowerCase();
8283             switch(direction){
8284                 case "l":
8285                 case "left":
8286                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8287                     break;
8288                case "r":
8289                case "right":
8290                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8291                     break;
8292                case "t":
8293                case "top":
8294                case "up":
8295                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8296                     break;
8297                case "b":
8298                case "bottom":
8299                case "down":
8300                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8301                     break;
8302             }
8303             return this;
8304         },
8305
8306         /**
8307          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8308          * @return {Roo.Element} this
8309          */
8310         clip : function(){
8311             if(!this.isClipped){
8312                this.isClipped = true;
8313                this.originalClip = {
8314                    "o": this.getStyle("overflow"),
8315                    "x": this.getStyle("overflow-x"),
8316                    "y": this.getStyle("overflow-y")
8317                };
8318                this.setStyle("overflow", "hidden");
8319                this.setStyle("overflow-x", "hidden");
8320                this.setStyle("overflow-y", "hidden");
8321             }
8322             return this;
8323         },
8324
8325         /**
8326          *  Return clipping (overflow) to original clipping before clip() was called
8327          * @return {Roo.Element} this
8328          */
8329         unclip : function(){
8330             if(this.isClipped){
8331                 this.isClipped = false;
8332                 var o = this.originalClip;
8333                 if(o.o){this.setStyle("overflow", o.o);}
8334                 if(o.x){this.setStyle("overflow-x", o.x);}
8335                 if(o.y){this.setStyle("overflow-y", o.y);}
8336             }
8337             return this;
8338         },
8339
8340
8341         /**
8342          * Gets the x,y coordinates specified by the anchor position on the element.
8343          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8344          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8345          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8346          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8347          * @return {Array} [x, y] An array containing the element's x and y coordinates
8348          */
8349         getAnchorXY : function(anchor, local, s){
8350             //Passing a different size is useful for pre-calculating anchors,
8351             //especially for anchored animations that change the el size.
8352
8353             var w, h, vp = false;
8354             if(!s){
8355                 var d = this.dom;
8356                 if(d == document.body || d == document){
8357                     vp = true;
8358                     w = D.getViewWidth(); h = D.getViewHeight();
8359                 }else{
8360                     w = this.getWidth(); h = this.getHeight();
8361                 }
8362             }else{
8363                 w = s.width;  h = s.height;
8364             }
8365             var x = 0, y = 0, r = Math.round;
8366             switch((anchor || "tl").toLowerCase()){
8367                 case "c":
8368                     x = r(w*.5);
8369                     y = r(h*.5);
8370                 break;
8371                 case "t":
8372                     x = r(w*.5);
8373                     y = 0;
8374                 break;
8375                 case "l":
8376                     x = 0;
8377                     y = r(h*.5);
8378                 break;
8379                 case "r":
8380                     x = w;
8381                     y = r(h*.5);
8382                 break;
8383                 case "b":
8384                     x = r(w*.5);
8385                     y = h;
8386                 break;
8387                 case "tl":
8388                     x = 0;
8389                     y = 0;
8390                 break;
8391                 case "bl":
8392                     x = 0;
8393                     y = h;
8394                 break;
8395                 case "br":
8396                     x = w;
8397                     y = h;
8398                 break;
8399                 case "tr":
8400                     x = w;
8401                     y = 0;
8402                 break;
8403             }
8404             if(local === true){
8405                 return [x, y];
8406             }
8407             if(vp){
8408                 var sc = this.getScroll();
8409                 return [x + sc.left, y + sc.top];
8410             }
8411             //Add the element's offset xy
8412             var o = this.getXY();
8413             return [x+o[0], y+o[1]];
8414         },
8415
8416         /**
8417          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8418          * supported position values.
8419          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8420          * @param {String} position The position to align to.
8421          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8422          * @return {Array} [x, y]
8423          */
8424         getAlignToXY : function(el, p, o){
8425             el = Roo.get(el);
8426             var d = this.dom;
8427             if(!el.dom){
8428                 throw "Element.alignTo with an element that doesn't exist";
8429             }
8430             var c = false; //constrain to viewport
8431             var p1 = "", p2 = "";
8432             o = o || [0,0];
8433
8434             if(!p){
8435                 p = "tl-bl";
8436             }else if(p == "?"){
8437                 p = "tl-bl?";
8438             }else if(p.indexOf("-") == -1){
8439                 p = "tl-" + p;
8440             }
8441             p = p.toLowerCase();
8442             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8443             if(!m){
8444                throw "Element.alignTo with an invalid alignment " + p;
8445             }
8446             p1 = m[1]; p2 = m[2]; c = !!m[3];
8447
8448             //Subtract the aligned el's internal xy from the target's offset xy
8449             //plus custom offset to get the aligned el's new offset xy
8450             var a1 = this.getAnchorXY(p1, true);
8451             var a2 = el.getAnchorXY(p2, false);
8452             var x = a2[0] - a1[0] + o[0];
8453             var y = a2[1] - a1[1] + o[1];
8454             if(c){
8455                 //constrain the aligned el to viewport if necessary
8456                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8457                 // 5px of margin for ie
8458                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8459
8460                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8461                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8462                 //otherwise swap the aligned el to the opposite border of the target.
8463                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8464                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8465                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8466                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8467
8468                var doc = document;
8469                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8470                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8471
8472                if((x+w) > dw + scrollX){
8473                     x = swapX ? r.left-w : dw+scrollX-w;
8474                 }
8475                if(x < scrollX){
8476                    x = swapX ? r.right : scrollX;
8477                }
8478                if((y+h) > dh + scrollY){
8479                     y = swapY ? r.top-h : dh+scrollY-h;
8480                 }
8481                if (y < scrollY){
8482                    y = swapY ? r.bottom : scrollY;
8483                }
8484             }
8485             return [x,y];
8486         },
8487
8488         // private
8489         getConstrainToXY : function(){
8490             var os = {top:0, left:0, bottom:0, right: 0};
8491
8492             return function(el, local, offsets, proposedXY){
8493                 el = Roo.get(el);
8494                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8495
8496                 var vw, vh, vx = 0, vy = 0;
8497                 if(el.dom == document.body || el.dom == document){
8498                     vw = Roo.lib.Dom.getViewWidth();
8499                     vh = Roo.lib.Dom.getViewHeight();
8500                 }else{
8501                     vw = el.dom.clientWidth;
8502                     vh = el.dom.clientHeight;
8503                     if(!local){
8504                         var vxy = el.getXY();
8505                         vx = vxy[0];
8506                         vy = vxy[1];
8507                     }
8508                 }
8509
8510                 var s = el.getScroll();
8511
8512                 vx += offsets.left + s.left;
8513                 vy += offsets.top + s.top;
8514
8515                 vw -= offsets.right;
8516                 vh -= offsets.bottom;
8517
8518                 var vr = vx+vw;
8519                 var vb = vy+vh;
8520
8521                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8522                 var x = xy[0], y = xy[1];
8523                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8524
8525                 // only move it if it needs it
8526                 var moved = false;
8527
8528                 // first validate right/bottom
8529                 if((x + w) > vr){
8530                     x = vr - w;
8531                     moved = true;
8532                 }
8533                 if((y + h) > vb){
8534                     y = vb - h;
8535                     moved = true;
8536                 }
8537                 // then make sure top/left isn't negative
8538                 if(x < vx){
8539                     x = vx;
8540                     moved = true;
8541                 }
8542                 if(y < vy){
8543                     y = vy;
8544                     moved = true;
8545                 }
8546                 return moved ? [x, y] : false;
8547             };
8548         }(),
8549
8550         // private
8551         adjustForConstraints : function(xy, parent, offsets){
8552             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8553         },
8554
8555         /**
8556          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8557          * document it aligns it to the viewport.
8558          * The position parameter is optional, and can be specified in any one of the following formats:
8559          * <ul>
8560          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8561          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8562          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8563          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8564          *   <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
8565          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8566          * </ul>
8567          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8568          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8569          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8570          * that specified in order to enforce the viewport constraints.
8571          * Following are all of the supported anchor positions:
8572     <pre>
8573     Value  Description
8574     -----  -----------------------------
8575     tl     The top left corner (default)
8576     t      The center of the top edge
8577     tr     The top right corner
8578     l      The center of the left edge
8579     c      In the center of the element
8580     r      The center of the right edge
8581     bl     The bottom left corner
8582     b      The center of the bottom edge
8583     br     The bottom right corner
8584     </pre>
8585     Example Usage:
8586     <pre><code>
8587     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8588     el.alignTo("other-el");
8589
8590     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8591     el.alignTo("other-el", "tr?");
8592
8593     // align the bottom right corner of el with the center left edge of other-el
8594     el.alignTo("other-el", "br-l?");
8595
8596     // align the center of el with the bottom left corner of other-el and
8597     // adjust the x position by -6 pixels (and the y position by 0)
8598     el.alignTo("other-el", "c-bl", [-6, 0]);
8599     </code></pre>
8600          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8601          * @param {String} position The position to align to.
8602          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8603          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8604          * @return {Roo.Element} this
8605          */
8606         alignTo : function(element, position, offsets, animate){
8607             var xy = this.getAlignToXY(element, position, offsets);
8608             this.setXY(xy, this.preanim(arguments, 3));
8609             return this;
8610         },
8611
8612         /**
8613          * Anchors an element to another element and realigns it when the window is resized.
8614          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8615          * @param {String} position The position to align to.
8616          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8617          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8618          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8619          * is a number, it is used as the buffer delay (defaults to 50ms).
8620          * @param {Function} callback The function to call after the animation finishes
8621          * @return {Roo.Element} this
8622          */
8623         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8624             var action = function(){
8625                 this.alignTo(el, alignment, offsets, animate);
8626                 Roo.callback(callback, this);
8627             };
8628             Roo.EventManager.onWindowResize(action, this);
8629             var tm = typeof monitorScroll;
8630             if(tm != 'undefined'){
8631                 Roo.EventManager.on(window, 'scroll', action, this,
8632                     {buffer: tm == 'number' ? monitorScroll : 50});
8633             }
8634             action.call(this); // align immediately
8635             return this;
8636         },
8637         /**
8638          * Clears any opacity settings from this element. Required in some cases for IE.
8639          * @return {Roo.Element} this
8640          */
8641         clearOpacity : function(){
8642             if (window.ActiveXObject) {
8643                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8644                     this.dom.style.filter = "";
8645                 }
8646             } else {
8647                 this.dom.style.opacity = "";
8648                 this.dom.style["-moz-opacity"] = "";
8649                 this.dom.style["-khtml-opacity"] = "";
8650             }
8651             return this;
8652         },
8653
8654         /**
8655          * Hide 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         hide : function(animate){
8660             this.setVisible(false, this.preanim(arguments, 0));
8661             return this;
8662         },
8663
8664         /**
8665         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8666         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8667          * @return {Roo.Element} this
8668          */
8669         show : function(animate){
8670             this.setVisible(true, this.preanim(arguments, 0));
8671             return this;
8672         },
8673
8674         /**
8675          * @private Test if size has a unit, otherwise appends the default
8676          */
8677         addUnits : function(size){
8678             return Roo.Element.addUnits(size, this.defaultUnit);
8679         },
8680
8681         /**
8682          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8683          * @return {Roo.Element} this
8684          */
8685         beginMeasure : function(){
8686             var el = this.dom;
8687             if(el.offsetWidth || el.offsetHeight){
8688                 return this; // offsets work already
8689             }
8690             var changed = [];
8691             var p = this.dom, b = document.body; // start with this element
8692             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8693                 var pe = Roo.get(p);
8694                 if(pe.getStyle('display') == 'none'){
8695                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8696                     p.style.visibility = "hidden";
8697                     p.style.display = "block";
8698                 }
8699                 p = p.parentNode;
8700             }
8701             this._measureChanged = changed;
8702             return this;
8703
8704         },
8705
8706         /**
8707          * Restores displays to before beginMeasure was called
8708          * @return {Roo.Element} this
8709          */
8710         endMeasure : function(){
8711             var changed = this._measureChanged;
8712             if(changed){
8713                 for(var i = 0, len = changed.length; i < len; i++) {
8714                     var r = changed[i];
8715                     r.el.style.visibility = r.visibility;
8716                     r.el.style.display = "none";
8717                 }
8718                 this._measureChanged = null;
8719             }
8720             return this;
8721         },
8722
8723         /**
8724         * Update the innerHTML of this element, optionally searching for and processing scripts
8725         * @param {String} html The new HTML
8726         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8727         * @param {Function} callback For async script loading you can be noticed when the update completes
8728         * @return {Roo.Element} this
8729          */
8730         update : function(html, loadScripts, callback){
8731             if(typeof html == "undefined"){
8732                 html = "";
8733             }
8734             if(loadScripts !== true){
8735                 this.dom.innerHTML = html;
8736                 if(typeof callback == "function"){
8737                     callback();
8738                 }
8739                 return this;
8740             }
8741             var id = Roo.id();
8742             var dom = this.dom;
8743
8744             html += '<span id="' + id + '"></span>';
8745
8746             E.onAvailable(id, function(){
8747                 var hd = document.getElementsByTagName("head")[0];
8748                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8749                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8750                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8751
8752                 var match;
8753                 while(match = re.exec(html)){
8754                     var attrs = match[1];
8755                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8756                     if(srcMatch && srcMatch[2]){
8757                        var s = document.createElement("script");
8758                        s.src = srcMatch[2];
8759                        var typeMatch = attrs.match(typeRe);
8760                        if(typeMatch && typeMatch[2]){
8761                            s.type = typeMatch[2];
8762                        }
8763                        hd.appendChild(s);
8764                     }else if(match[2] && match[2].length > 0){
8765                         if(window.execScript) {
8766                            window.execScript(match[2]);
8767                         } else {
8768                             /**
8769                              * eval:var:id
8770                              * eval:var:dom
8771                              * eval:var:html
8772                              * 
8773                              */
8774                            window.eval(match[2]);
8775                         }
8776                     }
8777                 }
8778                 var el = document.getElementById(id);
8779                 if(el){el.parentNode.removeChild(el);}
8780                 if(typeof callback == "function"){
8781                     callback();
8782                 }
8783             });
8784             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8785             return this;
8786         },
8787
8788         /**
8789          * Direct access to the UpdateManager update() method (takes the same parameters).
8790          * @param {String/Function} url The url for this request or a function to call to get the url
8791          * @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}
8792          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8793          * @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.
8794          * @return {Roo.Element} this
8795          */
8796         load : function(){
8797             var um = this.getUpdateManager();
8798             um.update.apply(um, arguments);
8799             return this;
8800         },
8801
8802         /**
8803         * Gets this element's UpdateManager
8804         * @return {Roo.UpdateManager} The UpdateManager
8805         */
8806         getUpdateManager : function(){
8807             if(!this.updateManager){
8808                 this.updateManager = new Roo.UpdateManager(this);
8809             }
8810             return this.updateManager;
8811         },
8812
8813         /**
8814          * Disables text selection for this element (normalized across browsers)
8815          * @return {Roo.Element} this
8816          */
8817         unselectable : function(){
8818             this.dom.unselectable = "on";
8819             this.swallowEvent("selectstart", true);
8820             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8821             this.addClass("x-unselectable");
8822             return this;
8823         },
8824
8825         /**
8826         * Calculates the x, y to center this element on the screen
8827         * @return {Array} The x, y values [x, y]
8828         */
8829         getCenterXY : function(){
8830             return this.getAlignToXY(document, 'c-c');
8831         },
8832
8833         /**
8834         * Centers the Element in either the viewport, or another Element.
8835         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8836         */
8837         center : function(centerIn){
8838             this.alignTo(centerIn || document, 'c-c');
8839             return this;
8840         },
8841
8842         /**
8843          * Tests various css rules/browsers to determine if this element uses a border box
8844          * @return {Boolean}
8845          */
8846         isBorderBox : function(){
8847             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8848         },
8849
8850         /**
8851          * Return a box {x, y, width, height} that can be used to set another elements
8852          * size/location to match this element.
8853          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8854          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8855          * @return {Object} box An object in the format {x, y, width, height}
8856          */
8857         getBox : function(contentBox, local){
8858             var xy;
8859             if(!local){
8860                 xy = this.getXY();
8861             }else{
8862                 var left = parseInt(this.getStyle("left"), 10) || 0;
8863                 var top = parseInt(this.getStyle("top"), 10) || 0;
8864                 xy = [left, top];
8865             }
8866             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8867             if(!contentBox){
8868                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8869             }else{
8870                 var l = this.getBorderWidth("l")+this.getPadding("l");
8871                 var r = this.getBorderWidth("r")+this.getPadding("r");
8872                 var t = this.getBorderWidth("t")+this.getPadding("t");
8873                 var b = this.getBorderWidth("b")+this.getPadding("b");
8874                 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)};
8875             }
8876             bx.right = bx.x + bx.width;
8877             bx.bottom = bx.y + bx.height;
8878             return bx;
8879         },
8880
8881         /**
8882          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8883          for more information about the sides.
8884          * @param {String} sides
8885          * @return {Number}
8886          */
8887         getFrameWidth : function(sides, onlyContentBox){
8888             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8889         },
8890
8891         /**
8892          * 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.
8893          * @param {Object} box The box to fill {x, y, width, height}
8894          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8895          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8896          * @return {Roo.Element} this
8897          */
8898         setBox : function(box, adjust, animate){
8899             var w = box.width, h = box.height;
8900             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8901                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8902                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8903             }
8904             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8905             return this;
8906         },
8907
8908         /**
8909          * Forces the browser to repaint this element
8910          * @return {Roo.Element} this
8911          */
8912          repaint : function(){
8913             var dom = this.dom;
8914             this.addClass("x-repaint");
8915             setTimeout(function(){
8916                 Roo.get(dom).removeClass("x-repaint");
8917             }, 1);
8918             return this;
8919         },
8920
8921         /**
8922          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8923          * then it returns the calculated width of the sides (see getPadding)
8924          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8925          * @return {Object/Number}
8926          */
8927         getMargins : function(side){
8928             if(!side){
8929                 return {
8930                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8931                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8932                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8933                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8934                 };
8935             }else{
8936                 return this.addStyles(side, El.margins);
8937              }
8938         },
8939
8940         // private
8941         addStyles : function(sides, styles){
8942             var val = 0, v, w;
8943             for(var i = 0, len = sides.length; i < len; i++){
8944                 v = this.getStyle(styles[sides.charAt(i)]);
8945                 if(v){
8946                      w = parseInt(v, 10);
8947                      if(w){ val += w; }
8948                 }
8949             }
8950             return val;
8951         },
8952
8953         /**
8954          * Creates a proxy element of this element
8955          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8956          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8957          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8958          * @return {Roo.Element} The new proxy element
8959          */
8960         createProxy : function(config, renderTo, matchBox){
8961             if(renderTo){
8962                 renderTo = Roo.getDom(renderTo);
8963             }else{
8964                 renderTo = document.body;
8965             }
8966             config = typeof config == "object" ?
8967                 config : {tag : "div", cls: config};
8968             var proxy = Roo.DomHelper.append(renderTo, config, true);
8969             if(matchBox){
8970                proxy.setBox(this.getBox());
8971             }
8972             return proxy;
8973         },
8974
8975         /**
8976          * Puts a mask over this element to disable user interaction. Requires core.css.
8977          * This method can only be applied to elements which accept child nodes.
8978          * @param {String} msg (optional) A message to display in the mask
8979          * @param {String} msgCls (optional) A css class to apply to the msg element
8980          * @return {Element} The mask  element
8981          */
8982         mask : function(msg, msgCls)
8983         {
8984             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8985                 this.setStyle("position", "relative");
8986             }
8987             if(!this._mask){
8988                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8989             }
8990             this.addClass("x-masked");
8991             this._mask.setDisplayed(true);
8992             
8993             // we wander
8994             var z = 0;
8995             var dom = this.dom
8996             while (dom && dom.style) {
8997                 if (!isNaN(parseInt(dom.style.zIndex))) {
8998                     z = Math.max(z, parseInt(dom.style.zIndex));
8999                 }
9000                 dom = dom.parentNode;
9001             }
9002             // if we are masking the body - then it hides everything..
9003             if (this.dom == document.body) {
9004                 z = 1000000;
9005                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9006                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9007             }
9008            
9009             if(typeof msg == 'string'){
9010                 if(!this._maskMsg){
9011                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9012                 }
9013                 var mm = this._maskMsg;
9014                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9015                 if (mm.dom.firstChild) { // weird IE issue?
9016                     mm.dom.firstChild.innerHTML = msg;
9017                 }
9018                 mm.setDisplayed(true);
9019                 mm.center(this);
9020                 mm.setStyle('z-index', z + 102);
9021             }
9022             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9023                 this._mask.setHeight(this.getHeight());
9024             }
9025             this._mask.setStyle('z-index', z + 100);
9026             
9027             return this._mask;
9028         },
9029
9030         /**
9031          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9032          * it is cached for reuse.
9033          */
9034         unmask : function(removeEl){
9035             if(this._mask){
9036                 if(removeEl === true){
9037                     this._mask.remove();
9038                     delete this._mask;
9039                     if(this._maskMsg){
9040                         this._maskMsg.remove();
9041                         delete this._maskMsg;
9042                     }
9043                 }else{
9044                     this._mask.setDisplayed(false);
9045                     if(this._maskMsg){
9046                         this._maskMsg.setDisplayed(false);
9047                     }
9048                 }
9049             }
9050             this.removeClass("x-masked");
9051         },
9052
9053         /**
9054          * Returns true if this element is masked
9055          * @return {Boolean}
9056          */
9057         isMasked : function(){
9058             return this._mask && this._mask.isVisible();
9059         },
9060
9061         /**
9062          * Creates an iframe shim for this element to keep selects and other windowed objects from
9063          * showing through.
9064          * @return {Roo.Element} The new shim element
9065          */
9066         createShim : function(){
9067             var el = document.createElement('iframe');
9068             el.frameBorder = 'no';
9069             el.className = 'roo-shim';
9070             if(Roo.isIE && Roo.isSecure){
9071                 el.src = Roo.SSL_SECURE_URL;
9072             }
9073             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9074             shim.autoBoxAdjust = false;
9075             return shim;
9076         },
9077
9078         /**
9079          * Removes this element from the DOM and deletes it from the cache
9080          */
9081         remove : function(){
9082             if(this.dom.parentNode){
9083                 this.dom.parentNode.removeChild(this.dom);
9084             }
9085             delete El.cache[this.dom.id];
9086         },
9087
9088         /**
9089          * Sets up event handlers to add and remove a css class when the mouse is over this element
9090          * @param {String} className
9091          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9092          * mouseout events for children elements
9093          * @return {Roo.Element} this
9094          */
9095         addClassOnOver : function(className, preventFlicker){
9096             this.on("mouseover", function(){
9097                 Roo.fly(this, '_internal').addClass(className);
9098             }, this.dom);
9099             var removeFn = function(e){
9100                 if(preventFlicker !== true || !e.within(this, true)){
9101                     Roo.fly(this, '_internal').removeClass(className);
9102                 }
9103             };
9104             this.on("mouseout", removeFn, this.dom);
9105             return this;
9106         },
9107
9108         /**
9109          * Sets up event handlers to add and remove a css class when this element has the focus
9110          * @param {String} className
9111          * @return {Roo.Element} this
9112          */
9113         addClassOnFocus : function(className){
9114             this.on("focus", function(){
9115                 Roo.fly(this, '_internal').addClass(className);
9116             }, this.dom);
9117             this.on("blur", function(){
9118                 Roo.fly(this, '_internal').removeClass(className);
9119             }, this.dom);
9120             return this;
9121         },
9122         /**
9123          * 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)
9124          * @param {String} className
9125          * @return {Roo.Element} this
9126          */
9127         addClassOnClick : function(className){
9128             var dom = this.dom;
9129             this.on("mousedown", function(){
9130                 Roo.fly(dom, '_internal').addClass(className);
9131                 var d = Roo.get(document);
9132                 var fn = function(){
9133                     Roo.fly(dom, '_internal').removeClass(className);
9134                     d.removeListener("mouseup", fn);
9135                 };
9136                 d.on("mouseup", fn);
9137             });
9138             return this;
9139         },
9140
9141         /**
9142          * Stops the specified event from bubbling and optionally prevents the default action
9143          * @param {String} eventName
9144          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9145          * @return {Roo.Element} this
9146          */
9147         swallowEvent : function(eventName, preventDefault){
9148             var fn = function(e){
9149                 e.stopPropagation();
9150                 if(preventDefault){
9151                     e.preventDefault();
9152                 }
9153             };
9154             if(eventName instanceof Array){
9155                 for(var i = 0, len = eventName.length; i < len; i++){
9156                      this.on(eventName[i], fn);
9157                 }
9158                 return this;
9159             }
9160             this.on(eventName, fn);
9161             return this;
9162         },
9163
9164         /**
9165          * @private
9166          */
9167       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9168
9169         /**
9170          * Sizes this element to its parent element's dimensions performing
9171          * neccessary box adjustments.
9172          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9173          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9174          * @return {Roo.Element} this
9175          */
9176         fitToParent : function(monitorResize, targetParent) {
9177           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9178           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9179           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9180             return;
9181           }
9182           var p = Roo.get(targetParent || this.dom.parentNode);
9183           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9184           if (monitorResize === true) {
9185             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9186             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9187           }
9188           return this;
9189         },
9190
9191         /**
9192          * Gets the next sibling, skipping text nodes
9193          * @return {HTMLElement} The next sibling or null
9194          */
9195         getNextSibling : function(){
9196             var n = this.dom.nextSibling;
9197             while(n && n.nodeType != 1){
9198                 n = n.nextSibling;
9199             }
9200             return n;
9201         },
9202
9203         /**
9204          * Gets the previous sibling, skipping text nodes
9205          * @return {HTMLElement} The previous sibling or null
9206          */
9207         getPrevSibling : function(){
9208             var n = this.dom.previousSibling;
9209             while(n && n.nodeType != 1){
9210                 n = n.previousSibling;
9211             }
9212             return n;
9213         },
9214
9215
9216         /**
9217          * Appends the passed element(s) to this element
9218          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9219          * @return {Roo.Element} this
9220          */
9221         appendChild: function(el){
9222             el = Roo.get(el);
9223             el.appendTo(this);
9224             return this;
9225         },
9226
9227         /**
9228          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9229          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9230          * automatically generated with the specified attributes.
9231          * @param {HTMLElement} insertBefore (optional) a child element of this element
9232          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9233          * @return {Roo.Element} The new child element
9234          */
9235         createChild: function(config, insertBefore, returnDom){
9236             config = config || {tag:'div'};
9237             if(insertBefore){
9238                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9239             }
9240             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9241         },
9242
9243         /**
9244          * Appends this element to the passed element
9245          * @param {String/HTMLElement/Element} el The new parent element
9246          * @return {Roo.Element} this
9247          */
9248         appendTo: function(el){
9249             el = Roo.getDom(el);
9250             el.appendChild(this.dom);
9251             return this;
9252         },
9253
9254         /**
9255          * Inserts this element before the passed element in the DOM
9256          * @param {String/HTMLElement/Element} el The element to insert before
9257          * @return {Roo.Element} this
9258          */
9259         insertBefore: function(el){
9260             el = Roo.getDom(el);
9261             el.parentNode.insertBefore(this.dom, el);
9262             return this;
9263         },
9264
9265         /**
9266          * Inserts this element after the passed element in the DOM
9267          * @param {String/HTMLElement/Element} el The element to insert after
9268          * @return {Roo.Element} this
9269          */
9270         insertAfter: function(el){
9271             el = Roo.getDom(el);
9272             el.parentNode.insertBefore(this.dom, el.nextSibling);
9273             return this;
9274         },
9275
9276         /**
9277          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9278          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9279          * @return {Roo.Element} The new child
9280          */
9281         insertFirst: function(el, returnDom){
9282             el = el || {};
9283             if(typeof el == 'object' && !el.nodeType){ // dh config
9284                 return this.createChild(el, this.dom.firstChild, returnDom);
9285             }else{
9286                 el = Roo.getDom(el);
9287                 this.dom.insertBefore(el, this.dom.firstChild);
9288                 return !returnDom ? Roo.get(el) : el;
9289             }
9290         },
9291
9292         /**
9293          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9294          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9295          * @param {String} where (optional) 'before' or 'after' defaults to before
9296          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9297          * @return {Roo.Element} the inserted Element
9298          */
9299         insertSibling: function(el, where, returnDom){
9300             where = where ? where.toLowerCase() : 'before';
9301             el = el || {};
9302             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9303
9304             if(typeof el == 'object' && !el.nodeType){ // dh config
9305                 if(where == 'after' && !this.dom.nextSibling){
9306                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9307                 }else{
9308                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9309                 }
9310
9311             }else{
9312                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9313                             where == 'before' ? this.dom : this.dom.nextSibling);
9314                 if(!returnDom){
9315                     rt = Roo.get(rt);
9316                 }
9317             }
9318             return rt;
9319         },
9320
9321         /**
9322          * Creates and wraps this element with another element
9323          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9324          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9325          * @return {HTMLElement/Element} The newly created wrapper element
9326          */
9327         wrap: function(config, returnDom){
9328             if(!config){
9329                 config = {tag: "div"};
9330             }
9331             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9332             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9333             return newEl;
9334         },
9335
9336         /**
9337          * Replaces the passed element with this element
9338          * @param {String/HTMLElement/Element} el The element to replace
9339          * @return {Roo.Element} this
9340          */
9341         replace: function(el){
9342             el = Roo.get(el);
9343             this.insertBefore(el);
9344             el.remove();
9345             return this;
9346         },
9347
9348         /**
9349          * Inserts an html fragment into this element
9350          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9351          * @param {String} html The HTML fragment
9352          * @param {Boolean} returnEl True to return an Roo.Element
9353          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9354          */
9355         insertHtml : function(where, html, returnEl){
9356             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9357             return returnEl ? Roo.get(el) : el;
9358         },
9359
9360         /**
9361          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9362          * @param {Object} o The object with the attributes
9363          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9364          * @return {Roo.Element} this
9365          */
9366         set : function(o, useSet){
9367             var el = this.dom;
9368             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9369             for(var attr in o){
9370                 if(attr == "style" || typeof o[attr] == "function") continue;
9371                 if(attr=="cls"){
9372                     el.className = o["cls"];
9373                 }else{
9374                     if(useSet) el.setAttribute(attr, o[attr]);
9375                     else el[attr] = o[attr];
9376                 }
9377             }
9378             if(o.style){
9379                 Roo.DomHelper.applyStyles(el, o.style);
9380             }
9381             return this;
9382         },
9383
9384         /**
9385          * Convenience method for constructing a KeyMap
9386          * @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:
9387          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9388          * @param {Function} fn The function to call
9389          * @param {Object} scope (optional) The scope of the function
9390          * @return {Roo.KeyMap} The KeyMap created
9391          */
9392         addKeyListener : function(key, fn, scope){
9393             var config;
9394             if(typeof key != "object" || key instanceof Array){
9395                 config = {
9396                     key: key,
9397                     fn: fn,
9398                     scope: scope
9399                 };
9400             }else{
9401                 config = {
9402                     key : key.key,
9403                     shift : key.shift,
9404                     ctrl : key.ctrl,
9405                     alt : key.alt,
9406                     fn: fn,
9407                     scope: scope
9408                 };
9409             }
9410             return new Roo.KeyMap(this, config);
9411         },
9412
9413         /**
9414          * Creates a KeyMap for this element
9415          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9416          * @return {Roo.KeyMap} The KeyMap created
9417          */
9418         addKeyMap : function(config){
9419             return new Roo.KeyMap(this, config);
9420         },
9421
9422         /**
9423          * Returns true if this element is scrollable.
9424          * @return {Boolean}
9425          */
9426          isScrollable : function(){
9427             var dom = this.dom;
9428             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9429         },
9430
9431         /**
9432          * 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().
9433          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9434          * @param {Number} value The new scroll value
9435          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9436          * @return {Element} this
9437          */
9438
9439         scrollTo : function(side, value, animate){
9440             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9441             if(!animate || !A){
9442                 this.dom[prop] = value;
9443             }else{
9444                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9445                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9446             }
9447             return this;
9448         },
9449
9450         /**
9451          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9452          * within this element's scrollable range.
9453          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9454          * @param {Number} distance How far to scroll the element in pixels
9455          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9456          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9457          * was scrolled as far as it could go.
9458          */
9459          scroll : function(direction, distance, animate){
9460              if(!this.isScrollable()){
9461                  return;
9462              }
9463              var el = this.dom;
9464              var l = el.scrollLeft, t = el.scrollTop;
9465              var w = el.scrollWidth, h = el.scrollHeight;
9466              var cw = el.clientWidth, ch = el.clientHeight;
9467              direction = direction.toLowerCase();
9468              var scrolled = false;
9469              var a = this.preanim(arguments, 2);
9470              switch(direction){
9471                  case "l":
9472                  case "left":
9473                      if(w - l > cw){
9474                          var v = Math.min(l + distance, w-cw);
9475                          this.scrollTo("left", v, a);
9476                          scrolled = true;
9477                      }
9478                      break;
9479                 case "r":
9480                 case "right":
9481                      if(l > 0){
9482                          var v = Math.max(l - distance, 0);
9483                          this.scrollTo("left", v, a);
9484                          scrolled = true;
9485                      }
9486                      break;
9487                 case "t":
9488                 case "top":
9489                 case "up":
9490                      if(t > 0){
9491                          var v = Math.max(t - distance, 0);
9492                          this.scrollTo("top", v, a);
9493                          scrolled = true;
9494                      }
9495                      break;
9496                 case "b":
9497                 case "bottom":
9498                 case "down":
9499                      if(h - t > ch){
9500                          var v = Math.min(t + distance, h-ch);
9501                          this.scrollTo("top", v, a);
9502                          scrolled = true;
9503                      }
9504                      break;
9505              }
9506              return scrolled;
9507         },
9508
9509         /**
9510          * Translates the passed page coordinates into left/top css values for this element
9511          * @param {Number/Array} x The page x or an array containing [x, y]
9512          * @param {Number} y The page y
9513          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9514          */
9515         translatePoints : function(x, y){
9516             if(typeof x == 'object' || x instanceof Array){
9517                 y = x[1]; x = x[0];
9518             }
9519             var p = this.getStyle('position');
9520             var o = this.getXY();
9521
9522             var l = parseInt(this.getStyle('left'), 10);
9523             var t = parseInt(this.getStyle('top'), 10);
9524
9525             if(isNaN(l)){
9526                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9527             }
9528             if(isNaN(t)){
9529                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9530             }
9531
9532             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9533         },
9534
9535         /**
9536          * Returns the current scroll position of the element.
9537          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9538          */
9539         getScroll : function(){
9540             var d = this.dom, doc = document;
9541             if(d == doc || d == doc.body){
9542                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9543                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9544                 return {left: l, top: t};
9545             }else{
9546                 return {left: d.scrollLeft, top: d.scrollTop};
9547             }
9548         },
9549
9550         /**
9551          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9552          * are convert to standard 6 digit hex color.
9553          * @param {String} attr The css attribute
9554          * @param {String} defaultValue The default value to use when a valid color isn't found
9555          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9556          * YUI color anims.
9557          */
9558         getColor : function(attr, defaultValue, prefix){
9559             var v = this.getStyle(attr);
9560             if(!v || v == "transparent" || v == "inherit") {
9561                 return defaultValue;
9562             }
9563             var color = typeof prefix == "undefined" ? "#" : prefix;
9564             if(v.substr(0, 4) == "rgb("){
9565                 var rvs = v.slice(4, v.length -1).split(",");
9566                 for(var i = 0; i < 3; i++){
9567                     var h = parseInt(rvs[i]).toString(16);
9568                     if(h < 16){
9569                         h = "0" + h;
9570                     }
9571                     color += h;
9572                 }
9573             } else {
9574                 if(v.substr(0, 1) == "#"){
9575                     if(v.length == 4) {
9576                         for(var i = 1; i < 4; i++){
9577                             var c = v.charAt(i);
9578                             color +=  c + c;
9579                         }
9580                     }else if(v.length == 7){
9581                         color += v.substr(1);
9582                     }
9583                 }
9584             }
9585             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9586         },
9587
9588         /**
9589          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9590          * gradient background, rounded corners and a 4-way shadow.
9591          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9592          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9593          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9594          * @return {Roo.Element} this
9595          */
9596         boxWrap : function(cls){
9597             cls = cls || 'x-box';
9598             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9599             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9600             return el;
9601         },
9602
9603         /**
9604          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9605          * @param {String} namespace The namespace in which to look for the attribute
9606          * @param {String} name The attribute name
9607          * @return {String} The attribute value
9608          */
9609         getAttributeNS : Roo.isIE ? function(ns, name){
9610             var d = this.dom;
9611             var type = typeof d[ns+":"+name];
9612             if(type != 'undefined' && type != 'unknown'){
9613                 return d[ns+":"+name];
9614             }
9615             return d[name];
9616         } : function(ns, name){
9617             var d = this.dom;
9618             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9619         },
9620         
9621         
9622         /**
9623          * Sets or Returns the value the dom attribute value
9624          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9625          * @param {String} value (optional) The value to set the attribute to
9626          * @return {String} The attribute value
9627          */
9628         attr : function(name){
9629             if (arguments.length > 1) {
9630                 this.dom.setAttribute(name, arguments[1]);
9631                 return arguments[1];
9632             }
9633             if (typeof(name) == 'object') {
9634                 for(var i in name) {
9635                     this.attr(i, name[i]);
9636                 }
9637                 return name;
9638             }
9639             
9640             
9641             if (!this.dom.hasAttribute(name)) {
9642                 return undefined;
9643             }
9644             return this.dom.getAttribute(name);
9645         }
9646         
9647         
9648         
9649     };
9650
9651     var ep = El.prototype;
9652
9653     /**
9654      * Appends an event handler (Shorthand for addListener)
9655      * @param {String}   eventName     The type of event to append
9656      * @param {Function} fn        The method the event invokes
9657      * @param {Object} scope       (optional) The scope (this object) of the fn
9658      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9659      * @method
9660      */
9661     ep.on = ep.addListener;
9662         // backwards compat
9663     ep.mon = ep.addListener;
9664
9665     /**
9666      * Removes an event handler from this element (shorthand for removeListener)
9667      * @param {String} eventName the type of event to remove
9668      * @param {Function} fn the method the event invokes
9669      * @return {Roo.Element} this
9670      * @method
9671      */
9672     ep.un = ep.removeListener;
9673
9674     /**
9675      * true to automatically adjust width and height settings for box-model issues (default to true)
9676      */
9677     ep.autoBoxAdjust = true;
9678
9679     // private
9680     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9681
9682     // private
9683     El.addUnits = function(v, defaultUnit){
9684         if(v === "" || v == "auto"){
9685             return v;
9686         }
9687         if(v === undefined){
9688             return '';
9689         }
9690         if(typeof v == "number" || !El.unitPattern.test(v)){
9691             return v + (defaultUnit || 'px');
9692         }
9693         return v;
9694     };
9695
9696     // special markup used throughout Roo when box wrapping elements
9697     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>';
9698     /**
9699      * Visibility mode constant - Use visibility to hide element
9700      * @static
9701      * @type Number
9702      */
9703     El.VISIBILITY = 1;
9704     /**
9705      * Visibility mode constant - Use display to hide element
9706      * @static
9707      * @type Number
9708      */
9709     El.DISPLAY = 2;
9710
9711     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9712     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9713     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9714
9715
9716
9717     /**
9718      * @private
9719      */
9720     El.cache = {};
9721
9722     var docEl;
9723
9724     /**
9725      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9726      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9727      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9728      * @return {Element} The Element object
9729      * @static
9730      */
9731     El.get = function(el){
9732         var ex, elm, id;
9733         if(!el){ return null; }
9734         if(typeof el == "string"){ // element id
9735             if(!(elm = document.getElementById(el))){
9736                 return null;
9737             }
9738             if(ex = El.cache[el]){
9739                 ex.dom = elm;
9740             }else{
9741                 ex = El.cache[el] = new El(elm);
9742             }
9743             return ex;
9744         }else if(el.tagName){ // dom element
9745             if(!(id = el.id)){
9746                 id = Roo.id(el);
9747             }
9748             if(ex = El.cache[id]){
9749                 ex.dom = el;
9750             }else{
9751                 ex = El.cache[id] = new El(el);
9752             }
9753             return ex;
9754         }else if(el instanceof El){
9755             if(el != docEl){
9756                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9757                                                               // catch case where it hasn't been appended
9758                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9759             }
9760             return el;
9761         }else if(el.isComposite){
9762             return el;
9763         }else if(el instanceof Array){
9764             return El.select(el);
9765         }else if(el == document){
9766             // create a bogus element object representing the document object
9767             if(!docEl){
9768                 var f = function(){};
9769                 f.prototype = El.prototype;
9770                 docEl = new f();
9771                 docEl.dom = document;
9772             }
9773             return docEl;
9774         }
9775         return null;
9776     };
9777
9778     // private
9779     El.uncache = function(el){
9780         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9781             if(a[i]){
9782                 delete El.cache[a[i].id || a[i]];
9783             }
9784         }
9785     };
9786
9787     // private
9788     // Garbage collection - uncache elements/purge listeners on orphaned elements
9789     // so we don't hold a reference and cause the browser to retain them
9790     El.garbageCollect = function(){
9791         if(!Roo.enableGarbageCollector){
9792             clearInterval(El.collectorThread);
9793             return;
9794         }
9795         for(var eid in El.cache){
9796             var el = El.cache[eid], d = el.dom;
9797             // -------------------------------------------------------
9798             // Determining what is garbage:
9799             // -------------------------------------------------------
9800             // !d
9801             // dom node is null, definitely garbage
9802             // -------------------------------------------------------
9803             // !d.parentNode
9804             // no parentNode == direct orphan, definitely garbage
9805             // -------------------------------------------------------
9806             // !d.offsetParent && !document.getElementById(eid)
9807             // display none elements have no offsetParent so we will
9808             // also try to look it up by it's id. However, check
9809             // offsetParent first so we don't do unneeded lookups.
9810             // This enables collection of elements that are not orphans
9811             // directly, but somewhere up the line they have an orphan
9812             // parent.
9813             // -------------------------------------------------------
9814             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9815                 delete El.cache[eid];
9816                 if(d && Roo.enableListenerCollection){
9817                     E.purgeElement(d);
9818                 }
9819             }
9820         }
9821     }
9822     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9823
9824
9825     // dom is optional
9826     El.Flyweight = function(dom){
9827         this.dom = dom;
9828     };
9829     El.Flyweight.prototype = El.prototype;
9830
9831     El._flyweights = {};
9832     /**
9833      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9834      * the dom node can be overwritten by other code.
9835      * @param {String/HTMLElement} el The dom node or id
9836      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9837      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9838      * @static
9839      * @return {Element} The shared Element object
9840      */
9841     El.fly = function(el, named){
9842         named = named || '_global';
9843         el = Roo.getDom(el);
9844         if(!el){
9845             return null;
9846         }
9847         if(!El._flyweights[named]){
9848             El._flyweights[named] = new El.Flyweight();
9849         }
9850         El._flyweights[named].dom = el;
9851         return El._flyweights[named];
9852     };
9853
9854     /**
9855      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9856      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9857      * Shorthand of {@link Roo.Element#get}
9858      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9859      * @return {Element} The Element object
9860      * @member Roo
9861      * @method get
9862      */
9863     Roo.get = El.get;
9864     /**
9865      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9866      * the dom node can be overwritten by other code.
9867      * Shorthand of {@link Roo.Element#fly}
9868      * @param {String/HTMLElement} el The dom node or id
9869      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9870      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9871      * @static
9872      * @return {Element} The shared Element object
9873      * @member Roo
9874      * @method fly
9875      */
9876     Roo.fly = El.fly;
9877
9878     // speedy lookup for elements never to box adjust
9879     var noBoxAdjust = Roo.isStrict ? {
9880         select:1
9881     } : {
9882         input:1, select:1, textarea:1
9883     };
9884     if(Roo.isIE || Roo.isGecko){
9885         noBoxAdjust['button'] = 1;
9886     }
9887
9888
9889     Roo.EventManager.on(window, 'unload', function(){
9890         delete El.cache;
9891         delete El._flyweights;
9892     });
9893 })();
9894
9895
9896
9897
9898 if(Roo.DomQuery){
9899     Roo.Element.selectorFunction = Roo.DomQuery.select;
9900 }
9901
9902 Roo.Element.select = function(selector, unique, root){
9903     var els;
9904     if(typeof selector == "string"){
9905         els = Roo.Element.selectorFunction(selector, root);
9906     }else if(selector.length !== undefined){
9907         els = selector;
9908     }else{
9909         throw "Invalid selector";
9910     }
9911     if(unique === true){
9912         return new Roo.CompositeElement(els);
9913     }else{
9914         return new Roo.CompositeElementLite(els);
9915     }
9916 };
9917 /**
9918  * Selects elements based on the passed CSS selector to enable working on them as 1.
9919  * @param {String/Array} selector The CSS selector or an array of elements
9920  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9921  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9922  * @return {CompositeElementLite/CompositeElement}
9923  * @member Roo
9924  * @method select
9925  */
9926 Roo.select = Roo.Element.select;
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939
9940
9941 /*
9942  * Based on:
9943  * Ext JS Library 1.1.1
9944  * Copyright(c) 2006-2007, Ext JS, LLC.
9945  *
9946  * Originally Released Under LGPL - original licence link has changed is not relivant.
9947  *
9948  * Fork - LGPL
9949  * <script type="text/javascript">
9950  */
9951
9952
9953
9954 //Notifies Element that fx methods are available
9955 Roo.enableFx = true;
9956
9957 /**
9958  * @class Roo.Fx
9959  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9960  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9961  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9962  * Element effects to work.</p><br/>
9963  *
9964  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9965  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9966  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9967  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9968  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9969  * expected results and should be done with care.</p><br/>
9970  *
9971  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9972  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9973 <pre>
9974 Value  Description
9975 -----  -----------------------------
9976 tl     The top left corner
9977 t      The center of the top edge
9978 tr     The top right corner
9979 l      The center of the left edge
9980 r      The center of the right edge
9981 bl     The bottom left corner
9982 b      The center of the bottom edge
9983 br     The bottom right corner
9984 </pre>
9985  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9986  * below are common options that can be passed to any Fx method.</b>
9987  * @cfg {Function} callback A function called when the effect is finished
9988  * @cfg {Object} scope The scope of the effect function
9989  * @cfg {String} easing A valid Easing value for the effect
9990  * @cfg {String} afterCls A css class to apply after the effect
9991  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9992  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9993  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9994  * effects that end with the element being visually hidden, ignored otherwise)
9995  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9996  * a function which returns such a specification that will be applied to the Element after the effect finishes
9997  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9998  * @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
9999  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10000  */
10001 Roo.Fx = {
10002         /**
10003          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10004          * origin for the slide effect.  This function automatically handles wrapping the element with
10005          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10006          * Usage:
10007          *<pre><code>
10008 // default: slide the element in from the top
10009 el.slideIn();
10010
10011 // custom: slide the element in from the right with a 2-second duration
10012 el.slideIn('r', { duration: 2 });
10013
10014 // common config options shown with default values
10015 el.slideIn('t', {
10016     easing: 'easeOut',
10017     duration: .5
10018 });
10019 </code></pre>
10020          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10021          * @param {Object} options (optional) Object literal with any of the Fx config options
10022          * @return {Roo.Element} The Element
10023          */
10024     slideIn : function(anchor, o){
10025         var el = this.getFxEl();
10026         o = o || {};
10027
10028         el.queueFx(o, function(){
10029
10030             anchor = anchor || "t";
10031
10032             // fix display to visibility
10033             this.fixDisplay();
10034
10035             // restore values after effect
10036             var r = this.getFxRestore();
10037             var b = this.getBox();
10038             // fixed size for slide
10039             this.setSize(b);
10040
10041             // wrap if needed
10042             var wrap = this.fxWrap(r.pos, o, "hidden");
10043
10044             var st = this.dom.style;
10045             st.visibility = "visible";
10046             st.position = "absolute";
10047
10048             // clear out temp styles after slide and unwrap
10049             var after = function(){
10050                 el.fxUnwrap(wrap, r.pos, o);
10051                 st.width = r.width;
10052                 st.height = r.height;
10053                 el.afterFx(o);
10054             };
10055             // time to calc the positions
10056             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10057
10058             switch(anchor.toLowerCase()){
10059                 case "t":
10060                     wrap.setSize(b.width, 0);
10061                     st.left = st.bottom = "0";
10062                     a = {height: bh};
10063                 break;
10064                 case "l":
10065                     wrap.setSize(0, b.height);
10066                     st.right = st.top = "0";
10067                     a = {width: bw};
10068                 break;
10069                 case "r":
10070                     wrap.setSize(0, b.height);
10071                     wrap.setX(b.right);
10072                     st.left = st.top = "0";
10073                     a = {width: bw, points: pt};
10074                 break;
10075                 case "b":
10076                     wrap.setSize(b.width, 0);
10077                     wrap.setY(b.bottom);
10078                     st.left = st.top = "0";
10079                     a = {height: bh, points: pt};
10080                 break;
10081                 case "tl":
10082                     wrap.setSize(0, 0);
10083                     st.right = st.bottom = "0";
10084                     a = {width: bw, height: bh};
10085                 break;
10086                 case "bl":
10087                     wrap.setSize(0, 0);
10088                     wrap.setY(b.y+b.height);
10089                     st.right = st.top = "0";
10090                     a = {width: bw, height: bh, points: pt};
10091                 break;
10092                 case "br":
10093                     wrap.setSize(0, 0);
10094                     wrap.setXY([b.right, b.bottom]);
10095                     st.left = st.top = "0";
10096                     a = {width: bw, height: bh, points: pt};
10097                 break;
10098                 case "tr":
10099                     wrap.setSize(0, 0);
10100                     wrap.setX(b.x+b.width);
10101                     st.left = st.bottom = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104             }
10105             this.dom.style.visibility = "visible";
10106             wrap.show();
10107
10108             arguments.callee.anim = wrap.fxanim(a,
10109                 o,
10110                 'motion',
10111                 .5,
10112                 'easeOut', after);
10113         });
10114         return this;
10115     },
10116     
10117         /**
10118          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10119          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10120          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10121          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10122          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10123          * Usage:
10124          *<pre><code>
10125 // default: slide the element out to the top
10126 el.slideOut();
10127
10128 // custom: slide the element out to the right with a 2-second duration
10129 el.slideOut('r', { duration: 2 });
10130
10131 // common config options shown with default values
10132 el.slideOut('t', {
10133     easing: 'easeOut',
10134     duration: .5,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10140          * @param {Object} options (optional) Object literal with any of the Fx config options
10141          * @return {Roo.Element} The Element
10142          */
10143     slideOut : function(anchor, o){
10144         var el = this.getFxEl();
10145         o = o || {};
10146
10147         el.queueFx(o, function(){
10148
10149             anchor = anchor || "t";
10150
10151             // restore values after effect
10152             var r = this.getFxRestore();
10153             
10154             var b = this.getBox();
10155             // fixed size for slide
10156             this.setSize(b);
10157
10158             // wrap if needed
10159             var wrap = this.fxWrap(r.pos, o, "visible");
10160
10161             var st = this.dom.style;
10162             st.visibility = "visible";
10163             st.position = "absolute";
10164
10165             wrap.setSize(b);
10166
10167             var after = function(){
10168                 if(o.useDisplay){
10169                     el.setDisplayed(false);
10170                 }else{
10171                     el.hide();
10172                 }
10173
10174                 el.fxUnwrap(wrap, r.pos, o);
10175
10176                 st.width = r.width;
10177                 st.height = r.height;
10178
10179                 el.afterFx(o);
10180             };
10181
10182             var a, zero = {to: 0};
10183             switch(anchor.toLowerCase()){
10184                 case "t":
10185                     st.left = st.bottom = "0";
10186                     a = {height: zero};
10187                 break;
10188                 case "l":
10189                     st.right = st.top = "0";
10190                     a = {width: zero};
10191                 break;
10192                 case "r":
10193                     st.left = st.top = "0";
10194                     a = {width: zero, points: {to:[b.right, b.y]}};
10195                 break;
10196                 case "b":
10197                     st.left = st.top = "0";
10198                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10199                 break;
10200                 case "tl":
10201                     st.right = st.bottom = "0";
10202                     a = {width: zero, height: zero};
10203                 break;
10204                 case "bl":
10205                     st.right = st.top = "0";
10206                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10207                 break;
10208                 case "br":
10209                     st.left = st.top = "0";
10210                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10211                 break;
10212                 case "tr":
10213                     st.left = st.bottom = "0";
10214                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10215                 break;
10216             }
10217
10218             arguments.callee.anim = wrap.fxanim(a,
10219                 o,
10220                 'motion',
10221                 .5,
10222                 "easeOut", after);
10223         });
10224         return this;
10225     },
10226
10227         /**
10228          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10229          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10230          * The element must be removed from the DOM using the 'remove' config option if desired.
10231          * Usage:
10232          *<pre><code>
10233 // default
10234 el.puff();
10235
10236 // common config options shown with default values
10237 el.puff({
10238     easing: 'easeOut',
10239     duration: .5,
10240     remove: false,
10241     useDisplay: false
10242 });
10243 </code></pre>
10244          * @param {Object} options (optional) Object literal with any of the Fx config options
10245          * @return {Roo.Element} The Element
10246          */
10247     puff : function(o){
10248         var el = this.getFxEl();
10249         o = o || {};
10250
10251         el.queueFx(o, function(){
10252             this.clearOpacity();
10253             this.show();
10254
10255             // restore values after effect
10256             var r = this.getFxRestore();
10257             var st = this.dom.style;
10258
10259             var after = function(){
10260                 if(o.useDisplay){
10261                     el.setDisplayed(false);
10262                 }else{
10263                     el.hide();
10264                 }
10265
10266                 el.clearOpacity();
10267
10268                 el.setPositioning(r.pos);
10269                 st.width = r.width;
10270                 st.height = r.height;
10271                 st.fontSize = '';
10272                 el.afterFx(o);
10273             };
10274
10275             var width = this.getWidth();
10276             var height = this.getHeight();
10277
10278             arguments.callee.anim = this.fxanim({
10279                     width : {to: this.adjustWidth(width * 2)},
10280                     height : {to: this.adjustHeight(height * 2)},
10281                     points : {by: [-(width * .5), -(height * .5)]},
10282                     opacity : {to: 0},
10283                     fontSize: {to:200, unit: "%"}
10284                 },
10285                 o,
10286                 'motion',
10287                 .5,
10288                 "easeOut", after);
10289         });
10290         return this;
10291     },
10292
10293         /**
10294          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10295          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10296          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10297          * Usage:
10298          *<pre><code>
10299 // default
10300 el.switchOff();
10301
10302 // all config options shown with default values
10303 el.switchOff({
10304     easing: 'easeIn',
10305     duration: .3,
10306     remove: false,
10307     useDisplay: false
10308 });
10309 </code></pre>
10310          * @param {Object} options (optional) Object literal with any of the Fx config options
10311          * @return {Roo.Element} The Element
10312          */
10313     switchOff : function(o){
10314         var el = this.getFxEl();
10315         o = o || {};
10316
10317         el.queueFx(o, function(){
10318             this.clearOpacity();
10319             this.clip();
10320
10321             // restore values after effect
10322             var r = this.getFxRestore();
10323             var st = this.dom.style;
10324
10325             var after = function(){
10326                 if(o.useDisplay){
10327                     el.setDisplayed(false);
10328                 }else{
10329                     el.hide();
10330                 }
10331
10332                 el.clearOpacity();
10333                 el.setPositioning(r.pos);
10334                 st.width = r.width;
10335                 st.height = r.height;
10336
10337                 el.afterFx(o);
10338             };
10339
10340             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10341                 this.clearOpacity();
10342                 (function(){
10343                     this.fxanim({
10344                         height:{to:1},
10345                         points:{by:[0, this.getHeight() * .5]}
10346                     }, o, 'motion', 0.3, 'easeIn', after);
10347                 }).defer(100, this);
10348             });
10349         });
10350         return this;
10351     },
10352
10353     /**
10354      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10355      * changed using the "attr" config option) and then fading back to the original color. If no original
10356      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10357      * Usage:
10358 <pre><code>
10359 // default: highlight background to yellow
10360 el.highlight();
10361
10362 // custom: highlight foreground text to blue for 2 seconds
10363 el.highlight("0000ff", { attr: 'color', duration: 2 });
10364
10365 // common config options shown with default values
10366 el.highlight("ffff9c", {
10367     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10368     endColor: (current color) or "ffffff",
10369     easing: 'easeIn',
10370     duration: 1
10371 });
10372 </code></pre>
10373      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10374      * @param {Object} options (optional) Object literal with any of the Fx config options
10375      * @return {Roo.Element} The Element
10376      */ 
10377     highlight : function(color, o){
10378         var el = this.getFxEl();
10379         o = o || {};
10380
10381         el.queueFx(o, function(){
10382             color = color || "ffff9c";
10383             attr = o.attr || "backgroundColor";
10384
10385             this.clearOpacity();
10386             this.show();
10387
10388             var origColor = this.getColor(attr);
10389             var restoreColor = this.dom.style[attr];
10390             endColor = (o.endColor || origColor) || "ffffff";
10391
10392             var after = function(){
10393                 el.dom.style[attr] = restoreColor;
10394                 el.afterFx(o);
10395             };
10396
10397             var a = {};
10398             a[attr] = {from: color, to: endColor};
10399             arguments.callee.anim = this.fxanim(a,
10400                 o,
10401                 'color',
10402                 1,
10403                 'easeIn', after);
10404         });
10405         return this;
10406     },
10407
10408    /**
10409     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10410     * Usage:
10411 <pre><code>
10412 // default: a single light blue ripple
10413 el.frame();
10414
10415 // custom: 3 red ripples lasting 3 seconds total
10416 el.frame("ff0000", 3, { duration: 3 });
10417
10418 // common config options shown with default values
10419 el.frame("C3DAF9", 1, {
10420     duration: 1 //duration of entire animation (not each individual ripple)
10421     // Note: Easing is not configurable and will be ignored if included
10422 });
10423 </code></pre>
10424     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10425     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     frame : function(color, count, o){
10430         var el = this.getFxEl();
10431         o = o || {};
10432
10433         el.queueFx(o, function(){
10434             color = color || "#C3DAF9";
10435             if(color.length == 6){
10436                 color = "#" + color;
10437             }
10438             count = count || 1;
10439             duration = o.duration || 1;
10440             this.show();
10441
10442             var b = this.getBox();
10443             var animFn = function(){
10444                 var proxy = this.createProxy({
10445
10446                      style:{
10447                         visbility:"hidden",
10448                         position:"absolute",
10449                         "z-index":"35000", // yee haw
10450                         border:"0px solid " + color
10451                      }
10452                   });
10453                 var scale = Roo.isBorderBox ? 2 : 1;
10454                 proxy.animate({
10455                     top:{from:b.y, to:b.y - 20},
10456                     left:{from:b.x, to:b.x - 20},
10457                     borderWidth:{from:0, to:10},
10458                     opacity:{from:1, to:0},
10459                     height:{from:b.height, to:(b.height + (20*scale))},
10460                     width:{from:b.width, to:(b.width + (20*scale))}
10461                 }, duration, function(){
10462                     proxy.remove();
10463                 });
10464                 if(--count > 0){
10465                      animFn.defer((duration/2)*1000, this);
10466                 }else{
10467                     el.afterFx(o);
10468                 }
10469             };
10470             animFn.call(this);
10471         });
10472         return this;
10473     },
10474
10475    /**
10476     * Creates a pause before any subsequent queued effects begin.  If there are
10477     * no effects queued after the pause it will have no effect.
10478     * Usage:
10479 <pre><code>
10480 el.pause(1);
10481 </code></pre>
10482     * @param {Number} seconds The length of time to pause (in seconds)
10483     * @return {Roo.Element} The Element
10484     */
10485     pause : function(seconds){
10486         var el = this.getFxEl();
10487         var o = {};
10488
10489         el.queueFx(o, function(){
10490             setTimeout(function(){
10491                 el.afterFx(o);
10492             }, seconds * 1000);
10493         });
10494         return this;
10495     },
10496
10497    /**
10498     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10499     * using the "endOpacity" config option.
10500     * Usage:
10501 <pre><code>
10502 // default: fade in from opacity 0 to 100%
10503 el.fadeIn();
10504
10505 // custom: fade in from opacity 0 to 75% over 2 seconds
10506 el.fadeIn({ endOpacity: .75, duration: 2});
10507
10508 // common config options shown with default values
10509 el.fadeIn({
10510     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10511     easing: 'easeOut',
10512     duration: .5
10513 });
10514 </code></pre>
10515     * @param {Object} options (optional) Object literal with any of the Fx config options
10516     * @return {Roo.Element} The Element
10517     */
10518     fadeIn : function(o){
10519         var el = this.getFxEl();
10520         o = o || {};
10521         el.queueFx(o, function(){
10522             this.setOpacity(0);
10523             this.fixDisplay();
10524             this.dom.style.visibility = 'visible';
10525             var to = o.endOpacity || 1;
10526             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10527                 o, null, .5, "easeOut", function(){
10528                 if(to == 1){
10529                     this.clearOpacity();
10530                 }
10531                 el.afterFx(o);
10532             });
10533         });
10534         return this;
10535     },
10536
10537    /**
10538     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10539     * using the "endOpacity" config option.
10540     * Usage:
10541 <pre><code>
10542 // default: fade out from the element's current opacity to 0
10543 el.fadeOut();
10544
10545 // custom: fade out from the element's current opacity to 25% over 2 seconds
10546 el.fadeOut({ endOpacity: .25, duration: 2});
10547
10548 // common config options shown with default values
10549 el.fadeOut({
10550     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10551     easing: 'easeOut',
10552     duration: .5
10553     remove: false,
10554     useDisplay: false
10555 });
10556 </code></pre>
10557     * @param {Object} options (optional) Object literal with any of the Fx config options
10558     * @return {Roo.Element} The Element
10559     */
10560     fadeOut : function(o){
10561         var el = this.getFxEl();
10562         o = o || {};
10563         el.queueFx(o, function(){
10564             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10565                 o, null, .5, "easeOut", function(){
10566                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10567                      this.dom.style.display = "none";
10568                 }else{
10569                      this.dom.style.visibility = "hidden";
10570                 }
10571                 this.clearOpacity();
10572                 el.afterFx(o);
10573             });
10574         });
10575         return this;
10576     },
10577
10578    /**
10579     * Animates the transition of an element's dimensions from a starting height/width
10580     * to an ending height/width.
10581     * Usage:
10582 <pre><code>
10583 // change height and width to 100x100 pixels
10584 el.scale(100, 100);
10585
10586 // common config options shown with default values.  The height and width will default to
10587 // the element's existing values if passed as null.
10588 el.scale(
10589     [element's width],
10590     [element's height], {
10591     easing: 'easeOut',
10592     duration: .35
10593 });
10594 </code></pre>
10595     * @param {Number} width  The new width (pass undefined to keep the original width)
10596     * @param {Number} height  The new height (pass undefined to keep the original height)
10597     * @param {Object} options (optional) Object literal with any of the Fx config options
10598     * @return {Roo.Element} The Element
10599     */
10600     scale : function(w, h, o){
10601         this.shift(Roo.apply({}, o, {
10602             width: w,
10603             height: h
10604         }));
10605         return this;
10606     },
10607
10608    /**
10609     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10610     * Any of these properties not specified in the config object will not be changed.  This effect 
10611     * requires that at least one new dimension, position or opacity setting must be passed in on
10612     * the config object in order for the function to have any effect.
10613     * Usage:
10614 <pre><code>
10615 // slide the element horizontally to x position 200 while changing the height and opacity
10616 el.shift({ x: 200, height: 50, opacity: .8 });
10617
10618 // common config options shown with default values.
10619 el.shift({
10620     width: [element's width],
10621     height: [element's height],
10622     x: [element's x position],
10623     y: [element's y position],
10624     opacity: [element's opacity],
10625     easing: 'easeOut',
10626     duration: .35
10627 });
10628 </code></pre>
10629     * @param {Object} options  Object literal with any of the Fx config options
10630     * @return {Roo.Element} The Element
10631     */
10632     shift : function(o){
10633         var el = this.getFxEl();
10634         o = o || {};
10635         el.queueFx(o, function(){
10636             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10637             if(w !== undefined){
10638                 a.width = {to: this.adjustWidth(w)};
10639             }
10640             if(h !== undefined){
10641                 a.height = {to: this.adjustHeight(h)};
10642             }
10643             if(x !== undefined || y !== undefined){
10644                 a.points = {to: [
10645                     x !== undefined ? x : this.getX(),
10646                     y !== undefined ? y : this.getY()
10647                 ]};
10648             }
10649             if(op !== undefined){
10650                 a.opacity = {to: op};
10651             }
10652             if(o.xy !== undefined){
10653                 a.points = {to: o.xy};
10654             }
10655             arguments.callee.anim = this.fxanim(a,
10656                 o, 'motion', .35, "easeOut", function(){
10657                 el.afterFx(o);
10658             });
10659         });
10660         return this;
10661     },
10662
10663         /**
10664          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10665          * ending point of the effect.
10666          * Usage:
10667          *<pre><code>
10668 // default: slide the element downward while fading out
10669 el.ghost();
10670
10671 // custom: slide the element out to the right with a 2-second duration
10672 el.ghost('r', { duration: 2 });
10673
10674 // common config options shown with default values
10675 el.ghost('b', {
10676     easing: 'easeOut',
10677     duration: .5
10678     remove: false,
10679     useDisplay: false
10680 });
10681 </code></pre>
10682          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10683          * @param {Object} options (optional) Object literal with any of the Fx config options
10684          * @return {Roo.Element} The Element
10685          */
10686     ghost : function(anchor, o){
10687         var el = this.getFxEl();
10688         o = o || {};
10689
10690         el.queueFx(o, function(){
10691             anchor = anchor || "b";
10692
10693             // restore values after effect
10694             var r = this.getFxRestore();
10695             var w = this.getWidth(),
10696                 h = this.getHeight();
10697
10698             var st = this.dom.style;
10699
10700             var after = function(){
10701                 if(o.useDisplay){
10702                     el.setDisplayed(false);
10703                 }else{
10704                     el.hide();
10705                 }
10706
10707                 el.clearOpacity();
10708                 el.setPositioning(r.pos);
10709                 st.width = r.width;
10710                 st.height = r.height;
10711
10712                 el.afterFx(o);
10713             };
10714
10715             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10716             switch(anchor.toLowerCase()){
10717                 case "t":
10718                     pt.by = [0, -h];
10719                 break;
10720                 case "l":
10721                     pt.by = [-w, 0];
10722                 break;
10723                 case "r":
10724                     pt.by = [w, 0];
10725                 break;
10726                 case "b":
10727                     pt.by = [0, h];
10728                 break;
10729                 case "tl":
10730                     pt.by = [-w, -h];
10731                 break;
10732                 case "bl":
10733                     pt.by = [-w, h];
10734                 break;
10735                 case "br":
10736                     pt.by = [w, h];
10737                 break;
10738                 case "tr":
10739                     pt.by = [w, -h];
10740                 break;
10741             }
10742
10743             arguments.callee.anim = this.fxanim(a,
10744                 o,
10745                 'motion',
10746                 .5,
10747                 "easeOut", after);
10748         });
10749         return this;
10750     },
10751
10752         /**
10753          * Ensures that all effects queued after syncFx is called on the element are
10754          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10755          * @return {Roo.Element} The Element
10756          */
10757     syncFx : function(){
10758         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10759             block : false,
10760             concurrent : true,
10761             stopFx : false
10762         });
10763         return this;
10764     },
10765
10766         /**
10767          * Ensures that all effects queued after sequenceFx is called on the element are
10768          * run in sequence.  This is the opposite of {@link #syncFx}.
10769          * @return {Roo.Element} The Element
10770          */
10771     sequenceFx : function(){
10772         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10773             block : false,
10774             concurrent : false,
10775             stopFx : false
10776         });
10777         return this;
10778     },
10779
10780         /* @private */
10781     nextFx : function(){
10782         var ef = this.fxQueue[0];
10783         if(ef){
10784             ef.call(this);
10785         }
10786     },
10787
10788         /**
10789          * Returns true if the element has any effects actively running or queued, else returns false.
10790          * @return {Boolean} True if element has active effects, else false
10791          */
10792     hasActiveFx : function(){
10793         return this.fxQueue && this.fxQueue[0];
10794     },
10795
10796         /**
10797          * Stops any running effects and clears the element's internal effects queue if it contains
10798          * any additional effects that haven't started yet.
10799          * @return {Roo.Element} The Element
10800          */
10801     stopFx : function(){
10802         if(this.hasActiveFx()){
10803             var cur = this.fxQueue[0];
10804             if(cur && cur.anim && cur.anim.isAnimated()){
10805                 this.fxQueue = [cur]; // clear out others
10806                 cur.anim.stop(true);
10807             }
10808         }
10809         return this;
10810     },
10811
10812         /* @private */
10813     beforeFx : function(o){
10814         if(this.hasActiveFx() && !o.concurrent){
10815            if(o.stopFx){
10816                this.stopFx();
10817                return true;
10818            }
10819            return false;
10820         }
10821         return true;
10822     },
10823
10824         /**
10825          * Returns true if the element is currently blocking so that no other effect can be queued
10826          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10827          * used to ensure that an effect initiated by a user action runs to completion prior to the
10828          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10829          * @return {Boolean} True if blocking, else false
10830          */
10831     hasFxBlock : function(){
10832         var q = this.fxQueue;
10833         return q && q[0] && q[0].block;
10834     },
10835
10836         /* @private */
10837     queueFx : function(o, fn){
10838         if(!this.fxQueue){
10839             this.fxQueue = [];
10840         }
10841         if(!this.hasFxBlock()){
10842             Roo.applyIf(o, this.fxDefaults);
10843             if(!o.concurrent){
10844                 var run = this.beforeFx(o);
10845                 fn.block = o.block;
10846                 this.fxQueue.push(fn);
10847                 if(run){
10848                     this.nextFx();
10849                 }
10850             }else{
10851                 fn.call(this);
10852             }
10853         }
10854         return this;
10855     },
10856
10857         /* @private */
10858     fxWrap : function(pos, o, vis){
10859         var wrap;
10860         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10861             var wrapXY;
10862             if(o.fixPosition){
10863                 wrapXY = this.getXY();
10864             }
10865             var div = document.createElement("div");
10866             div.style.visibility = vis;
10867             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10868             wrap.setPositioning(pos);
10869             if(wrap.getStyle("position") == "static"){
10870                 wrap.position("relative");
10871             }
10872             this.clearPositioning('auto');
10873             wrap.clip();
10874             wrap.dom.appendChild(this.dom);
10875             if(wrapXY){
10876                 wrap.setXY(wrapXY);
10877             }
10878         }
10879         return wrap;
10880     },
10881
10882         /* @private */
10883     fxUnwrap : function(wrap, pos, o){
10884         this.clearPositioning();
10885         this.setPositioning(pos);
10886         if(!o.wrap){
10887             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10888             wrap.remove();
10889         }
10890     },
10891
10892         /* @private */
10893     getFxRestore : function(){
10894         var st = this.dom.style;
10895         return {pos: this.getPositioning(), width: st.width, height : st.height};
10896     },
10897
10898         /* @private */
10899     afterFx : function(o){
10900         if(o.afterStyle){
10901             this.applyStyles(o.afterStyle);
10902         }
10903         if(o.afterCls){
10904             this.addClass(o.afterCls);
10905         }
10906         if(o.remove === true){
10907             this.remove();
10908         }
10909         Roo.callback(o.callback, o.scope, [this]);
10910         if(!o.concurrent){
10911             this.fxQueue.shift();
10912             this.nextFx();
10913         }
10914     },
10915
10916         /* @private */
10917     getFxEl : function(){ // support for composite element fx
10918         return Roo.get(this.dom);
10919     },
10920
10921         /* @private */
10922     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10923         animType = animType || 'run';
10924         opt = opt || {};
10925         var anim = Roo.lib.Anim[animType](
10926             this.dom, args,
10927             (opt.duration || defaultDur) || .35,
10928             (opt.easing || defaultEase) || 'easeOut',
10929             function(){
10930                 Roo.callback(cb, this);
10931             },
10932             this
10933         );
10934         opt.anim = anim;
10935         return anim;
10936     }
10937 };
10938
10939 // backwords compat
10940 Roo.Fx.resize = Roo.Fx.scale;
10941
10942 //When included, Roo.Fx is automatically applied to Element so that all basic
10943 //effects are available directly via the Element API
10944 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10945  * Based on:
10946  * Ext JS Library 1.1.1
10947  * Copyright(c) 2006-2007, Ext JS, LLC.
10948  *
10949  * Originally Released Under LGPL - original licence link has changed is not relivant.
10950  *
10951  * Fork - LGPL
10952  * <script type="text/javascript">
10953  */
10954
10955
10956 /**
10957  * @class Roo.CompositeElement
10958  * Standard composite class. Creates a Roo.Element for every element in the collection.
10959  * <br><br>
10960  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10961  * actions will be performed on all the elements in this collection.</b>
10962  * <br><br>
10963  * All methods return <i>this</i> and can be chained.
10964  <pre><code>
10965  var els = Roo.select("#some-el div.some-class", true);
10966  // or select directly from an existing element
10967  var el = Roo.get('some-el');
10968  el.select('div.some-class', true);
10969
10970  els.setWidth(100); // all elements become 100 width
10971  els.hide(true); // all elements fade out and hide
10972  // or
10973  els.setWidth(100).hide(true);
10974  </code></pre>
10975  */
10976 Roo.CompositeElement = function(els){
10977     this.elements = [];
10978     this.addElements(els);
10979 };
10980 Roo.CompositeElement.prototype = {
10981     isComposite: true,
10982     addElements : function(els){
10983         if(!els) return this;
10984         if(typeof els == "string"){
10985             els = Roo.Element.selectorFunction(els);
10986         }
10987         var yels = this.elements;
10988         var index = yels.length-1;
10989         for(var i = 0, len = els.length; i < len; i++) {
10990                 yels[++index] = Roo.get(els[i]);
10991         }
10992         return this;
10993     },
10994
10995     /**
10996     * Clears this composite and adds the elements returned by the passed selector.
10997     * @param {String/Array} els A string CSS selector, an array of elements or an element
10998     * @return {CompositeElement} this
10999     */
11000     fill : function(els){
11001         this.elements = [];
11002         this.add(els);
11003         return this;
11004     },
11005
11006     /**
11007     * Filters this composite to only elements that match the passed selector.
11008     * @param {String} selector A string CSS selector
11009     * @param {Boolean} inverse return inverse filter (not matches)
11010     * @return {CompositeElement} this
11011     */
11012     filter : function(selector, inverse){
11013         var els = [];
11014         inverse = inverse || false;
11015         this.each(function(el){
11016             var match = inverse ? !el.is(selector) : el.is(selector);
11017             if(match){
11018                 els[els.length] = el.dom;
11019             }
11020         });
11021         this.fill(els);
11022         return this;
11023     },
11024
11025     invoke : function(fn, args){
11026         var els = this.elements;
11027         for(var i = 0, len = els.length; i < len; i++) {
11028                 Roo.Element.prototype[fn].apply(els[i], args);
11029         }
11030         return this;
11031     },
11032     /**
11033     * Adds elements to this composite.
11034     * @param {String/Array} els A string CSS selector, an array of elements or an element
11035     * @return {CompositeElement} this
11036     */
11037     add : function(els){
11038         if(typeof els == "string"){
11039             this.addElements(Roo.Element.selectorFunction(els));
11040         }else if(els.length !== undefined){
11041             this.addElements(els);
11042         }else{
11043             this.addElements([els]);
11044         }
11045         return this;
11046     },
11047     /**
11048     * Calls the passed function passing (el, this, index) for each element in this composite.
11049     * @param {Function} fn The function to call
11050     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11051     * @return {CompositeElement} this
11052     */
11053     each : function(fn, scope){
11054         var els = this.elements;
11055         for(var i = 0, len = els.length; i < len; i++){
11056             if(fn.call(scope || els[i], els[i], this, i) === false) {
11057                 break;
11058             }
11059         }
11060         return this;
11061     },
11062
11063     /**
11064      * Returns the Element object at the specified index
11065      * @param {Number} index
11066      * @return {Roo.Element}
11067      */
11068     item : function(index){
11069         return this.elements[index] || null;
11070     },
11071
11072     /**
11073      * Returns the first Element
11074      * @return {Roo.Element}
11075      */
11076     first : function(){
11077         return this.item(0);
11078     },
11079
11080     /**
11081      * Returns the last Element
11082      * @return {Roo.Element}
11083      */
11084     last : function(){
11085         return this.item(this.elements.length-1);
11086     },
11087
11088     /**
11089      * Returns the number of elements in this composite
11090      * @return Number
11091      */
11092     getCount : function(){
11093         return this.elements.length;
11094     },
11095
11096     /**
11097      * Returns true if this composite contains the passed element
11098      * @return Boolean
11099      */
11100     contains : function(el){
11101         return this.indexOf(el) !== -1;
11102     },
11103
11104     /**
11105      * Returns true if this composite contains the passed element
11106      * @return Boolean
11107      */
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.get(el));
11110     },
11111
11112
11113     /**
11114     * Removes the specified element(s).
11115     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11116     * or an array of any of those.
11117     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11118     * @return {CompositeElement} this
11119     */
11120     removeElement : function(el, removeDom){
11121         if(el instanceof Array){
11122             for(var i = 0, len = el.length; i < len; i++){
11123                 this.removeElement(el[i]);
11124             }
11125             return this;
11126         }
11127         var index = typeof el == 'number' ? el : this.indexOf(el);
11128         if(index !== -1){
11129             if(removeDom){
11130                 var d = this.elements[index];
11131                 if(d.dom){
11132                     d.remove();
11133                 }else{
11134                     d.parentNode.removeChild(d);
11135                 }
11136             }
11137             this.elements.splice(index, 1);
11138         }
11139         return this;
11140     },
11141
11142     /**
11143     * Replaces the specified element with the passed element.
11144     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11145     * to replace.
11146     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11147     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11148     * @return {CompositeElement} this
11149     */
11150     replaceElement : function(el, replacement, domReplace){
11151         var index = typeof el == 'number' ? el : this.indexOf(el);
11152         if(index !== -1){
11153             if(domReplace){
11154                 this.elements[index].replaceWith(replacement);
11155             }else{
11156                 this.elements.splice(index, 1, Roo.get(replacement))
11157             }
11158         }
11159         return this;
11160     },
11161
11162     /**
11163      * Removes all elements.
11164      */
11165     clear : function(){
11166         this.elements = [];
11167     }
11168 };
11169 (function(){
11170     Roo.CompositeElement.createCall = function(proto, fnName){
11171         if(!proto[fnName]){
11172             proto[fnName] = function(){
11173                 return this.invoke(fnName, arguments);
11174             };
11175         }
11176     };
11177     for(var fnName in Roo.Element.prototype){
11178         if(typeof Roo.Element.prototype[fnName] == "function"){
11179             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11180         }
11181     };
11182 })();
11183 /*
11184  * Based on:
11185  * Ext JS Library 1.1.1
11186  * Copyright(c) 2006-2007, Ext JS, LLC.
11187  *
11188  * Originally Released Under LGPL - original licence link has changed is not relivant.
11189  *
11190  * Fork - LGPL
11191  * <script type="text/javascript">
11192  */
11193
11194 /**
11195  * @class Roo.CompositeElementLite
11196  * @extends Roo.CompositeElement
11197  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11198  <pre><code>
11199  var els = Roo.select("#some-el div.some-class");
11200  // or select directly from an existing element
11201  var el = Roo.get('some-el');
11202  el.select('div.some-class');
11203
11204  els.setWidth(100); // all elements become 100 width
11205  els.hide(true); // all elements fade out and hide
11206  // or
11207  els.setWidth(100).hide(true);
11208  </code></pre><br><br>
11209  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11210  * actions will be performed on all the elements in this collection.</b>
11211  */
11212 Roo.CompositeElementLite = function(els){
11213     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11214     this.el = new Roo.Element.Flyweight();
11215 };
11216 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11217     addElements : function(els){
11218         if(els){
11219             if(els instanceof Array){
11220                 this.elements = this.elements.concat(els);
11221             }else{
11222                 var yels = this.elements;
11223                 var index = yels.length-1;
11224                 for(var i = 0, len = els.length; i < len; i++) {
11225                     yels[++index] = els[i];
11226                 }
11227             }
11228         }
11229         return this;
11230     },
11231     invoke : function(fn, args){
11232         var els = this.elements;
11233         var el = this.el;
11234         for(var i = 0, len = els.length; i < len; i++) {
11235             el.dom = els[i];
11236                 Roo.Element.prototype[fn].apply(el, args);
11237         }
11238         return this;
11239     },
11240     /**
11241      * Returns a flyweight Element of the dom element object at the specified index
11242      * @param {Number} index
11243      * @return {Roo.Element}
11244      */
11245     item : function(index){
11246         if(!this.elements[index]){
11247             return null;
11248         }
11249         this.el.dom = this.elements[index];
11250         return this.el;
11251     },
11252
11253     // fixes scope with flyweight
11254     addListener : function(eventName, handler, scope, opt){
11255         var els = this.elements;
11256         for(var i = 0, len = els.length; i < len; i++) {
11257             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11258         }
11259         return this;
11260     },
11261
11262     /**
11263     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11264     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11265     * a reference to the dom node, use el.dom.</b>
11266     * @param {Function} fn The function to call
11267     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11268     * @return {CompositeElement} this
11269     */
11270     each : function(fn, scope){
11271         var els = this.elements;
11272         var el = this.el;
11273         for(var i = 0, len = els.length; i < len; i++){
11274             el.dom = els[i];
11275                 if(fn.call(scope || el, el, this, i) === false){
11276                 break;
11277             }
11278         }
11279         return this;
11280     },
11281
11282     indexOf : function(el){
11283         return this.elements.indexOf(Roo.getDom(el));
11284     },
11285
11286     replaceElement : function(el, replacement, domReplace){
11287         var index = typeof el == 'number' ? el : this.indexOf(el);
11288         if(index !== -1){
11289             replacement = Roo.getDom(replacement);
11290             if(domReplace){
11291                 var d = this.elements[index];
11292                 d.parentNode.insertBefore(replacement, d);
11293                 d.parentNode.removeChild(d);
11294             }
11295             this.elements.splice(index, 1, replacement);
11296         }
11297         return this;
11298     }
11299 });
11300 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11301
11302 /*
11303  * Based on:
11304  * Ext JS Library 1.1.1
11305  * Copyright(c) 2006-2007, Ext JS, LLC.
11306  *
11307  * Originally Released Under LGPL - original licence link has changed is not relivant.
11308  *
11309  * Fork - LGPL
11310  * <script type="text/javascript">
11311  */
11312
11313  
11314
11315 /**
11316  * @class Roo.data.Connection
11317  * @extends Roo.util.Observable
11318  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11319  * either to a configured URL, or to a URL specified at request time.<br><br>
11320  * <p>
11321  * Requests made by this class are asynchronous, and will return immediately. No data from
11322  * the server will be available to the statement immediately following the {@link #request} call.
11323  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11324  * <p>
11325  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11326  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11327  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11328  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11329  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11330  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11331  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11332  * standard DOM methods.
11333  * @constructor
11334  * @param {Object} config a configuration object.
11335  */
11336 Roo.data.Connection = function(config){
11337     Roo.apply(this, config);
11338     this.addEvents({
11339         /**
11340          * @event beforerequest
11341          * Fires before a network request is made to retrieve a data object.
11342          * @param {Connection} conn This Connection object.
11343          * @param {Object} options The options config object passed to the {@link #request} method.
11344          */
11345         "beforerequest" : true,
11346         /**
11347          * @event requestcomplete
11348          * Fires if the request was successfully completed.
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         "requestcomplete" : true,
11355         /**
11356          * @event requestexception
11357          * Fires if an error HTTP status was returned from the server.
11358          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11359          * @param {Connection} conn This Connection object.
11360          * @param {Object} response The XHR object containing the response data.
11361          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11362          * @param {Object} options The options config object passed to the {@link #request} method.
11363          */
11364         "requestexception" : true
11365     });
11366     Roo.data.Connection.superclass.constructor.call(this);
11367 };
11368
11369 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11370     /**
11371      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11372      */
11373     /**
11374      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11375      * extra parameters to each request made by this object. (defaults to undefined)
11376      */
11377     /**
11378      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11379      *  to each request made by this object. (defaults to undefined)
11380      */
11381     /**
11382      * @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)
11383      */
11384     /**
11385      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11386      */
11387     timeout : 30000,
11388     /**
11389      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11390      * @type Boolean
11391      */
11392     autoAbort:false,
11393
11394     /**
11395      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11396      * @type Boolean
11397      */
11398     disableCaching: true,
11399
11400     /**
11401      * Sends an HTTP request to a remote server.
11402      * @param {Object} options An object which may contain the following properties:<ul>
11403      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11404      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11405      * request, a url encoded string or a function to call to get either.</li>
11406      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11407      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11408      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11409      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11410      * <li>options {Object} The parameter to the request call.</li>
11411      * <li>success {Boolean} True if the request succeeded.</li>
11412      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11413      * </ul></li>
11414      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11415      * The callback is passed the following parameters:<ul>
11416      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11417      * <li>options {Object} The parameter to the request call.</li>
11418      * </ul></li>
11419      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11420      * The callback is passed the following parameters:<ul>
11421      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11422      * <li>options {Object} The parameter to the request call.</li>
11423      * </ul></li>
11424      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11425      * for the callback function. Defaults to the browser window.</li>
11426      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11427      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11428      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11429      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11430      * params for the post data. Any params will be appended to the URL.</li>
11431      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11432      * </ul>
11433      * @return {Number} transactionId
11434      */
11435     request : function(o){
11436         if(this.fireEvent("beforerequest", this, o) !== false){
11437             var p = o.params;
11438
11439             if(typeof p == "function"){
11440                 p = p.call(o.scope||window, o);
11441             }
11442             if(typeof p == "object"){
11443                 p = Roo.urlEncode(o.params);
11444             }
11445             if(this.extraParams){
11446                 var extras = Roo.urlEncode(this.extraParams);
11447                 p = p ? (p + '&' + extras) : extras;
11448             }
11449
11450             var url = o.url || this.url;
11451             if(typeof url == 'function'){
11452                 url = url.call(o.scope||window, o);
11453             }
11454
11455             if(o.form){
11456                 var form = Roo.getDom(o.form);
11457                 url = url || form.action;
11458
11459                 var enctype = form.getAttribute("enctype");
11460                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11461                     return this.doFormUpload(o, p, url);
11462                 }
11463                 var f = Roo.lib.Ajax.serializeForm(form);
11464                 p = p ? (p + '&' + f) : f;
11465             }
11466
11467             var hs = o.headers;
11468             if(this.defaultHeaders){
11469                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11470                 if(!o.headers){
11471                     o.headers = hs;
11472                 }
11473             }
11474
11475             var cb = {
11476                 success: this.handleResponse,
11477                 failure: this.handleFailure,
11478                 scope: this,
11479                 argument: {options: o},
11480                 timeout : o.timeout || this.timeout
11481             };
11482
11483             var method = o.method||this.method||(p ? "POST" : "GET");
11484
11485             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11486                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11487             }
11488
11489             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11490                 if(o.autoAbort){
11491                     this.abort();
11492                 }
11493             }else if(this.autoAbort !== false){
11494                 this.abort();
11495             }
11496
11497             if((method == 'GET' && p) || o.xmlData){
11498                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11499                 p = '';
11500             }
11501             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11502             return this.transId;
11503         }else{
11504             Roo.callback(o.callback, o.scope, [o, null, null]);
11505             return null;
11506         }
11507     },
11508
11509     /**
11510      * Determine whether this object has a request outstanding.
11511      * @param {Number} transactionId (Optional) defaults to the last transaction
11512      * @return {Boolean} True if there is an outstanding request.
11513      */
11514     isLoading : function(transId){
11515         if(transId){
11516             return Roo.lib.Ajax.isCallInProgress(transId);
11517         }else{
11518             return this.transId ? true : false;
11519         }
11520     },
11521
11522     /**
11523      * Aborts any outstanding request.
11524      * @param {Number} transactionId (Optional) defaults to the last transaction
11525      */
11526     abort : function(transId){
11527         if(transId || this.isLoading()){
11528             Roo.lib.Ajax.abort(transId || this.transId);
11529         }
11530     },
11531
11532     // private
11533     handleResponse : function(response){
11534         this.transId = false;
11535         var options = response.argument.options;
11536         response.argument = options ? options.argument : null;
11537         this.fireEvent("requestcomplete", this, response, options);
11538         Roo.callback(options.success, options.scope, [response, options]);
11539         Roo.callback(options.callback, options.scope, [options, true, response]);
11540     },
11541
11542     // private
11543     handleFailure : function(response, e){
11544         this.transId = false;
11545         var options = response.argument.options;
11546         response.argument = options ? options.argument : null;
11547         this.fireEvent("requestexception", this, response, options, e);
11548         Roo.callback(options.failure, options.scope, [response, options]);
11549         Roo.callback(options.callback, options.scope, [options, false, response]);
11550     },
11551
11552     // private
11553     doFormUpload : function(o, ps, url){
11554         var id = Roo.id();
11555         var frame = document.createElement('iframe');
11556         frame.id = id;
11557         frame.name = id;
11558         frame.className = 'x-hidden';
11559         if(Roo.isIE){
11560             frame.src = Roo.SSL_SECURE_URL;
11561         }
11562         document.body.appendChild(frame);
11563
11564         if(Roo.isIE){
11565            document.frames[id].name = id;
11566         }
11567
11568         var form = Roo.getDom(o.form);
11569         form.target = id;
11570         form.method = 'POST';
11571         form.enctype = form.encoding = 'multipart/form-data';
11572         if(url){
11573             form.action = url;
11574         }
11575
11576         var hiddens, hd;
11577         if(ps){ // add dynamic params
11578             hiddens = [];
11579             ps = Roo.urlDecode(ps, false);
11580             for(var k in ps){
11581                 if(ps.hasOwnProperty(k)){
11582                     hd = document.createElement('input');
11583                     hd.type = 'hidden';
11584                     hd.name = k;
11585                     hd.value = ps[k];
11586                     form.appendChild(hd);
11587                     hiddens.push(hd);
11588                 }
11589             }
11590         }
11591
11592         function cb(){
11593             var r = {  // bogus response object
11594                 responseText : '',
11595                 responseXML : null
11596             };
11597
11598             r.argument = o ? o.argument : null;
11599
11600             try { //
11601                 var doc;
11602                 if(Roo.isIE){
11603                     doc = frame.contentWindow.document;
11604                 }else {
11605                     doc = (frame.contentDocument || window.frames[id].document);
11606                 }
11607                 if(doc && doc.body){
11608                     r.responseText = doc.body.innerHTML;
11609                 }
11610                 if(doc && doc.XMLDocument){
11611                     r.responseXML = doc.XMLDocument;
11612                 }else {
11613                     r.responseXML = doc;
11614                 }
11615             }
11616             catch(e) {
11617                 // ignore
11618             }
11619
11620             Roo.EventManager.removeListener(frame, 'load', cb, this);
11621
11622             this.fireEvent("requestcomplete", this, r, o);
11623             Roo.callback(o.success, o.scope, [r, o]);
11624             Roo.callback(o.callback, o.scope, [o, true, r]);
11625
11626             setTimeout(function(){document.body.removeChild(frame);}, 100);
11627         }
11628
11629         Roo.EventManager.on(frame, 'load', cb, this);
11630         form.submit();
11631
11632         if(hiddens){ // remove dynamic params
11633             for(var i = 0, len = hiddens.length; i < len; i++){
11634                 form.removeChild(hiddens[i]);
11635             }
11636         }
11637     }
11638 });
11639 /*
11640  * Based on:
11641  * Ext JS Library 1.1.1
11642  * Copyright(c) 2006-2007, Ext JS, LLC.
11643  *
11644  * Originally Released Under LGPL - original licence link has changed is not relivant.
11645  *
11646  * Fork - LGPL
11647  * <script type="text/javascript">
11648  */
11649  
11650 /**
11651  * Global Ajax request class.
11652  * 
11653  * @class Roo.Ajax
11654  * @extends Roo.data.Connection
11655  * @static
11656  * 
11657  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11658  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11659  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11660  * @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)
11661  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11662  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11663  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11664  */
11665 Roo.Ajax = new Roo.data.Connection({
11666     // fix up the docs
11667     /**
11668      * @scope Roo.Ajax
11669      * @type {Boolear} 
11670      */
11671     autoAbort : false,
11672
11673     /**
11674      * Serialize the passed form into a url encoded string
11675      * @scope Roo.Ajax
11676      * @param {String/HTMLElement} form
11677      * @return {String}
11678      */
11679     serializeForm : function(form){
11680         return Roo.lib.Ajax.serializeForm(form);
11681     }
11682 });/*
11683  * Based on:
11684  * Ext JS Library 1.1.1
11685  * Copyright(c) 2006-2007, Ext JS, LLC.
11686  *
11687  * Originally Released Under LGPL - original licence link has changed is not relivant.
11688  *
11689  * Fork - LGPL
11690  * <script type="text/javascript">
11691  */
11692
11693  
11694 /**
11695  * @class Roo.UpdateManager
11696  * @extends Roo.util.Observable
11697  * Provides AJAX-style update for Element object.<br><br>
11698  * Usage:<br>
11699  * <pre><code>
11700  * // Get it from a Roo.Element object
11701  * var el = Roo.get("foo");
11702  * var mgr = el.getUpdateManager();
11703  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11704  * ...
11705  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11706  * <br>
11707  * // or directly (returns the same UpdateManager instance)
11708  * var mgr = new Roo.UpdateManager("myElementId");
11709  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11710  * mgr.on("update", myFcnNeedsToKnow);
11711  * <br>
11712    // short handed call directly from the element object
11713    Roo.get("foo").load({
11714         url: "bar.php",
11715         scripts:true,
11716         params: "for=bar",
11717         text: "Loading Foo..."
11718    });
11719  * </code></pre>
11720  * @constructor
11721  * Create new UpdateManager directly.
11722  * @param {String/HTMLElement/Roo.Element} el The element to update
11723  * @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).
11724  */
11725 Roo.UpdateManager = function(el, forceNew){
11726     el = Roo.get(el);
11727     if(!forceNew && el.updateManager){
11728         return el.updateManager;
11729     }
11730     /**
11731      * The Element object
11732      * @type Roo.Element
11733      */
11734     this.el = el;
11735     /**
11736      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11737      * @type String
11738      */
11739     this.defaultUrl = null;
11740
11741     this.addEvents({
11742         /**
11743          * @event beforeupdate
11744          * Fired before an update is made, return false from your handler and the update is cancelled.
11745          * @param {Roo.Element} el
11746          * @param {String/Object/Function} url
11747          * @param {String/Object} params
11748          */
11749         "beforeupdate": true,
11750         /**
11751          * @event update
11752          * Fired after successful update is made.
11753          * @param {Roo.Element} el
11754          * @param {Object} oResponseObject The response Object
11755          */
11756         "update": true,
11757         /**
11758          * @event failure
11759          * Fired on update failure.
11760          * @param {Roo.Element} el
11761          * @param {Object} oResponseObject The response Object
11762          */
11763         "failure": true
11764     });
11765     var d = Roo.UpdateManager.defaults;
11766     /**
11767      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11768      * @type String
11769      */
11770     this.sslBlankUrl = d.sslBlankUrl;
11771     /**
11772      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11773      * @type Boolean
11774      */
11775     this.disableCaching = d.disableCaching;
11776     /**
11777      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11778      * @type String
11779      */
11780     this.indicatorText = d.indicatorText;
11781     /**
11782      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11783      * @type String
11784      */
11785     this.showLoadIndicator = d.showLoadIndicator;
11786     /**
11787      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11788      * @type Number
11789      */
11790     this.timeout = d.timeout;
11791
11792     /**
11793      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11794      * @type Boolean
11795      */
11796     this.loadScripts = d.loadScripts;
11797
11798     /**
11799      * Transaction object of current executing transaction
11800      */
11801     this.transaction = null;
11802
11803     /**
11804      * @private
11805      */
11806     this.autoRefreshProcId = null;
11807     /**
11808      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11809      * @type Function
11810      */
11811     this.refreshDelegate = this.refresh.createDelegate(this);
11812     /**
11813      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11814      * @type Function
11815      */
11816     this.updateDelegate = this.update.createDelegate(this);
11817     /**
11818      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11819      * @type Function
11820      */
11821     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11822     /**
11823      * @private
11824      */
11825     this.successDelegate = this.processSuccess.createDelegate(this);
11826     /**
11827      * @private
11828      */
11829     this.failureDelegate = this.processFailure.createDelegate(this);
11830
11831     if(!this.renderer){
11832      /**
11833       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11834       */
11835     this.renderer = new Roo.UpdateManager.BasicRenderer();
11836     }
11837     
11838     Roo.UpdateManager.superclass.constructor.call(this);
11839 };
11840
11841 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11842     /**
11843      * Get the Element this UpdateManager is bound to
11844      * @return {Roo.Element} The element
11845      */
11846     getEl : function(){
11847         return this.el;
11848     },
11849     /**
11850      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11851      * @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:
11852 <pre><code>
11853 um.update({<br/>
11854     url: "your-url.php",<br/>
11855     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11856     callback: yourFunction,<br/>
11857     scope: yourObject, //(optional scope)  <br/>
11858     discardUrl: false, <br/>
11859     nocache: false,<br/>
11860     text: "Loading...",<br/>
11861     timeout: 30,<br/>
11862     scripts: false<br/>
11863 });
11864 </code></pre>
11865      * The only required property is url. The optional properties nocache, text and scripts
11866      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11867      * @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}
11868      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11869      * @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.
11870      */
11871     update : function(url, params, callback, discardUrl){
11872         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11873             var method = this.method,
11874                 cfg;
11875             if(typeof url == "object"){ // must be config object
11876                 cfg = url;
11877                 url = cfg.url;
11878                 params = params || cfg.params;
11879                 callback = callback || cfg.callback;
11880                 discardUrl = discardUrl || cfg.discardUrl;
11881                 if(callback && cfg.scope){
11882                     callback = callback.createDelegate(cfg.scope);
11883                 }
11884                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11885                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11886                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11887                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11888                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11889             }
11890             this.showLoading();
11891             if(!discardUrl){
11892                 this.defaultUrl = url;
11893             }
11894             if(typeof url == "function"){
11895                 url = url.call(this);
11896             }
11897
11898             method = method || (params ? "POST" : "GET");
11899             if(method == "GET"){
11900                 url = this.prepareUrl(url);
11901             }
11902
11903             var o = Roo.apply(cfg ||{}, {
11904                 url : url,
11905                 params: params,
11906                 success: this.successDelegate,
11907                 failure: this.failureDelegate,
11908                 callback: undefined,
11909                 timeout: (this.timeout*1000),
11910                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11911             });
11912             Roo.log("updated manager called with timeout of " + o.timeout);
11913             this.transaction = Roo.Ajax.request(o);
11914         }
11915     },
11916
11917     /**
11918      * 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.
11919      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11920      * @param {String/HTMLElement} form The form Id or form element
11921      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11922      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11923      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11924      */
11925     formUpdate : function(form, url, reset, callback){
11926         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11927             if(typeof url == "function"){
11928                 url = url.call(this);
11929             }
11930             form = Roo.getDom(form);
11931             this.transaction = Roo.Ajax.request({
11932                 form: form,
11933                 url:url,
11934                 success: this.successDelegate,
11935                 failure: this.failureDelegate,
11936                 timeout: (this.timeout*1000),
11937                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11938             });
11939             this.showLoading.defer(1, this);
11940         }
11941     },
11942
11943     /**
11944      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11945      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11946      */
11947     refresh : function(callback){
11948         if(this.defaultUrl == null){
11949             return;
11950         }
11951         this.update(this.defaultUrl, null, callback, true);
11952     },
11953
11954     /**
11955      * Set this element to auto refresh.
11956      * @param {Number} interval How often to update (in seconds).
11957      * @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)
11958      * @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}
11959      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11960      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11961      */
11962     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11963         if(refreshNow){
11964             this.update(url || this.defaultUrl, params, callback, true);
11965         }
11966         if(this.autoRefreshProcId){
11967             clearInterval(this.autoRefreshProcId);
11968         }
11969         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11970     },
11971
11972     /**
11973      * Stop auto refresh on this element.
11974      */
11975      stopAutoRefresh : function(){
11976         if(this.autoRefreshProcId){
11977             clearInterval(this.autoRefreshProcId);
11978             delete this.autoRefreshProcId;
11979         }
11980     },
11981
11982     isAutoRefreshing : function(){
11983        return this.autoRefreshProcId ? true : false;
11984     },
11985     /**
11986      * Called to update the element to "Loading" state. Override to perform custom action.
11987      */
11988     showLoading : function(){
11989         if(this.showLoadIndicator){
11990             this.el.update(this.indicatorText);
11991         }
11992     },
11993
11994     /**
11995      * Adds unique parameter to query string if disableCaching = true
11996      * @private
11997      */
11998     prepareUrl : function(url){
11999         if(this.disableCaching){
12000             var append = "_dc=" + (new Date().getTime());
12001             if(url.indexOf("?") !== -1){
12002                 url += "&" + append;
12003             }else{
12004                 url += "?" + append;
12005             }
12006         }
12007         return url;
12008     },
12009
12010     /**
12011      * @private
12012      */
12013     processSuccess : function(response){
12014         this.transaction = null;
12015         if(response.argument.form && response.argument.reset){
12016             try{ // put in try/catch since some older FF releases had problems with this
12017                 response.argument.form.reset();
12018             }catch(e){}
12019         }
12020         if(this.loadScripts){
12021             this.renderer.render(this.el, response, this,
12022                 this.updateComplete.createDelegate(this, [response]));
12023         }else{
12024             this.renderer.render(this.el, response, this);
12025             this.updateComplete(response);
12026         }
12027     },
12028
12029     updateComplete : function(response){
12030         this.fireEvent("update", this.el, response);
12031         if(typeof response.argument.callback == "function"){
12032             response.argument.callback(this.el, true, response);
12033         }
12034     },
12035
12036     /**
12037      * @private
12038      */
12039     processFailure : function(response){
12040         this.transaction = null;
12041         this.fireEvent("failure", this.el, response);
12042         if(typeof response.argument.callback == "function"){
12043             response.argument.callback(this.el, false, response);
12044         }
12045     },
12046
12047     /**
12048      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12049      * @param {Object} renderer The object implementing the render() method
12050      */
12051     setRenderer : function(renderer){
12052         this.renderer = renderer;
12053     },
12054
12055     getRenderer : function(){
12056        return this.renderer;
12057     },
12058
12059     /**
12060      * Set the defaultUrl used for updates
12061      * @param {String/Function} defaultUrl The url or a function to call to get the url
12062      */
12063     setDefaultUrl : function(defaultUrl){
12064         this.defaultUrl = defaultUrl;
12065     },
12066
12067     /**
12068      * Aborts the executing transaction
12069      */
12070     abort : function(){
12071         if(this.transaction){
12072             Roo.Ajax.abort(this.transaction);
12073         }
12074     },
12075
12076     /**
12077      * Returns true if an update is in progress
12078      * @return {Boolean}
12079      */
12080     isUpdating : function(){
12081         if(this.transaction){
12082             return Roo.Ajax.isLoading(this.transaction);
12083         }
12084         return false;
12085     }
12086 });
12087
12088 /**
12089  * @class Roo.UpdateManager.defaults
12090  * @static (not really - but it helps the doc tool)
12091  * The defaults collection enables customizing the default properties of UpdateManager
12092  */
12093    Roo.UpdateManager.defaults = {
12094        /**
12095          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12096          * @type Number
12097          */
12098          timeout : 30,
12099
12100          /**
12101          * True to process scripts by default (Defaults to false).
12102          * @type Boolean
12103          */
12104         loadScripts : false,
12105
12106         /**
12107         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12108         * @type String
12109         */
12110         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12111         /**
12112          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12113          * @type Boolean
12114          */
12115         disableCaching : false,
12116         /**
12117          * Whether to show indicatorText when loading (Defaults to true).
12118          * @type Boolean
12119          */
12120         showLoadIndicator : true,
12121         /**
12122          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12123          * @type String
12124          */
12125         indicatorText : '<div class="loading-indicator">Loading...</div>'
12126    };
12127
12128 /**
12129  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12130  *Usage:
12131  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12132  * @param {String/HTMLElement/Roo.Element} el The element to update
12133  * @param {String} url The url
12134  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12135  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12136  * @static
12137  * @deprecated
12138  * @member Roo.UpdateManager
12139  */
12140 Roo.UpdateManager.updateElement = function(el, url, params, options){
12141     var um = Roo.get(el, true).getUpdateManager();
12142     Roo.apply(um, options);
12143     um.update(url, params, options ? options.callback : null);
12144 };
12145 // alias for backwards compat
12146 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12147 /**
12148  * @class Roo.UpdateManager.BasicRenderer
12149  * Default Content renderer. Updates the elements innerHTML with the responseText.
12150  */
12151 Roo.UpdateManager.BasicRenderer = function(){};
12152
12153 Roo.UpdateManager.BasicRenderer.prototype = {
12154     /**
12155      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12156      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12157      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12158      * @param {Roo.Element} el The element being rendered
12159      * @param {Object} response The YUI Connect response object
12160      * @param {UpdateManager} updateManager The calling update manager
12161      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12162      */
12163      render : function(el, response, updateManager, callback){
12164         el.update(response.responseText, updateManager.loadScripts, callback);
12165     }
12166 };
12167 /*
12168  * Based on:
12169  * Roo JS
12170  * (c)) Alan Knowles
12171  * Licence : LGPL
12172  */
12173
12174
12175 /**
12176  * @class Roo.DomTemplate
12177  * @extends Roo.Template
12178  * An effort at a dom based template engine..
12179  *
12180  * Similar to XTemplate, except it uses dom parsing to create the template..
12181  *
12182  * Supported features:
12183  *
12184  *  Tags:
12185
12186 <pre><code>
12187       {a_variable} - output encoded.
12188       {a_variable.format:("Y-m-d")} - call a method on the variable
12189       {a_variable:raw} - unencoded output
12190       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12191       {a_variable:this.method_on_template(...)} - call a method on the template object.
12192  
12193 </code></pre>
12194  *  The tpl tag:
12195 <pre><code>
12196         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12197         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12198         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12199         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12200   
12201 </code></pre>
12202  *      
12203  */
12204 Roo.DomTemplate = function()
12205 {
12206      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12207      if (this.html) {
12208         this.compile();
12209      }
12210 };
12211
12212
12213 Roo.extend(Roo.DomTemplate, Roo.Template, {
12214     /**
12215      * id counter for sub templates.
12216      */
12217     id : 0,
12218     /**
12219      * flag to indicate if dom parser is inside a pre,
12220      * it will strip whitespace if not.
12221      */
12222     inPre : false,
12223     
12224     /**
12225      * The various sub templates
12226      */
12227     tpls : false,
12228     
12229     
12230     
12231     /**
12232      *
12233      * basic tag replacing syntax
12234      * WORD:WORD()
12235      *
12236      * // you can fake an object call by doing this
12237      *  x.t:(test,tesT) 
12238      * 
12239      */
12240     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12241     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12242     
12243     iterChild : function (node, method) {
12244         
12245         var oldPre = this.inPre;
12246         if (node.tagName == 'PRE') {
12247             this.inPre = true;
12248         }
12249         for( var i = 0; i < node.childNodes.length; i++) {
12250             method.call(this, node.childNodes[i]);
12251         }
12252         this.inPre = oldPre;
12253     },
12254     
12255     
12256     
12257     /**
12258      * compile the template
12259      *
12260      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12261      *
12262      */
12263     compile: function()
12264     {
12265         var s = this.html;
12266         
12267         // covert the html into DOM...
12268         var doc = false;
12269         var div =false;
12270         try {
12271             doc = document.implementation.createHTMLDocument("");
12272             doc.documentElement.innerHTML =   this.html  ;
12273             div = doc.documentElement;
12274         } catch (e) {
12275             // old IE... - nasty -- it causes all sorts of issues.. with
12276             // images getting pulled from server..
12277             div = document.createElement('div');
12278             div.innerHTML = this.html;
12279         }
12280         //doc.documentElement.innerHTML = htmlBody
12281          
12282         
12283         
12284         this.tpls = [];
12285         var _t = this;
12286         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12287         
12288         var tpls = this.tpls;
12289         
12290         // create a top level template from the snippet..
12291         
12292         //Roo.log(div.innerHTML);
12293         
12294         var tpl = {
12295             uid : 'master',
12296             id : this.id++,
12297             attr : false,
12298             value : false,
12299             body : div.innerHTML,
12300             
12301             forCall : false,
12302             execCall : false,
12303             dom : div,
12304             isTop : true
12305             
12306         };
12307         tpls.unshift(tpl);
12308         
12309         
12310         // compile them...
12311         this.tpls = [];
12312         Roo.each(tpls, function(tp){
12313             this.compileTpl(tp);
12314             this.tpls[tp.id] = tp;
12315         }, this);
12316         
12317         this.master = tpls[0];
12318         return this;
12319         
12320         
12321     },
12322     
12323     compileNode : function(node, istop) {
12324         // test for
12325         //Roo.log(node);
12326         
12327         
12328         // skip anything not a tag..
12329         if (node.nodeType != 1) {
12330             if (node.nodeType == 3 && !this.inPre) {
12331                 // reduce white space..
12332                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12333                 
12334             }
12335             return;
12336         }
12337         
12338         var tpl = {
12339             uid : false,
12340             id : false,
12341             attr : false,
12342             value : false,
12343             body : '',
12344             
12345             forCall : false,
12346             execCall : false,
12347             dom : false,
12348             isTop : istop
12349             
12350             
12351         };
12352         
12353         
12354         switch(true) {
12355             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12356             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12357             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12358             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12359             // no default..
12360         }
12361         
12362         
12363         if (!tpl.attr) {
12364             // just itterate children..
12365             this.iterChild(node,this.compileNode);
12366             return;
12367         }
12368         tpl.uid = this.id++;
12369         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12370         node.removeAttribute('roo-'+ tpl.attr);
12371         if (tpl.attr != 'name') {
12372             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12373             node.parentNode.replaceChild(placeholder,  node);
12374         } else {
12375             
12376             var placeholder =  document.createElement('span');
12377             placeholder.className = 'roo-tpl-' + tpl.value;
12378             node.parentNode.replaceChild(placeholder,  node);
12379         }
12380         
12381         // parent now sees '{domtplXXXX}
12382         this.iterChild(node,this.compileNode);
12383         
12384         // we should now have node body...
12385         var div = document.createElement('div');
12386         div.appendChild(node);
12387         tpl.dom = node;
12388         // this has the unfortunate side effect of converting tagged attributes
12389         // eg. href="{...}" into %7C...%7D
12390         // this has been fixed by searching for those combo's although it's a bit hacky..
12391         
12392         
12393         tpl.body = div.innerHTML;
12394         
12395         
12396          
12397         tpl.id = tpl.uid;
12398         switch(tpl.attr) {
12399             case 'for' :
12400                 switch (tpl.value) {
12401                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12402                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12403                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12404                 }
12405                 break;
12406             
12407             case 'exec':
12408                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12409                 break;
12410             
12411             case 'if':     
12412                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12413                 break;
12414             
12415             case 'name':
12416                 tpl.id  = tpl.value; // replace non characters???
12417                 break;
12418             
12419         }
12420         
12421         
12422         this.tpls.push(tpl);
12423         
12424         
12425         
12426     },
12427     
12428     
12429     
12430     
12431     /**
12432      * Compile a segment of the template into a 'sub-template'
12433      *
12434      * 
12435      * 
12436      *
12437      */
12438     compileTpl : function(tpl)
12439     {
12440         var fm = Roo.util.Format;
12441         var useF = this.disableFormats !== true;
12442         
12443         var sep = Roo.isGecko ? "+\n" : ",\n";
12444         
12445         var undef = function(str) {
12446             Roo.debug && Roo.log("Property not found :"  + str);
12447             return '';
12448         };
12449           
12450         //Roo.log(tpl.body);
12451         
12452         
12453         
12454         var fn = function(m, lbrace, name, format, args)
12455         {
12456             //Roo.log("ARGS");
12457             //Roo.log(arguments);
12458             args = args ? args.replace(/\\'/g,"'") : args;
12459             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12460             if (typeof(format) == 'undefined') {
12461                 format =  'htmlEncode'; 
12462             }
12463             if (format == 'raw' ) {
12464                 format = false;
12465             }
12466             
12467             if(name.substr(0, 6) == 'domtpl'){
12468                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12469             }
12470             
12471             // build an array of options to determine if value is undefined..
12472             
12473             // basically get 'xxxx.yyyy' then do
12474             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12475             //    (function () { Roo.log("Property not found"); return ''; })() :
12476             //    ......
12477             
12478             var udef_ar = [];
12479             var lookfor = '';
12480             Roo.each(name.split('.'), function(st) {
12481                 lookfor += (lookfor.length ? '.': '') + st;
12482                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12483             });
12484             
12485             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12486             
12487             
12488             if(format && useF){
12489                 
12490                 args = args ? ',' + args : "";
12491                  
12492                 if(format.substr(0, 5) != "this."){
12493                     format = "fm." + format + '(';
12494                 }else{
12495                     format = 'this.call("'+ format.substr(5) + '", ';
12496                     args = ", values";
12497                 }
12498                 
12499                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12500             }
12501              
12502             if (args && args.length) {
12503                 // called with xxyx.yuu:(test,test)
12504                 // change to ()
12505                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12506             }
12507             // raw.. - :raw modifier..
12508             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12509             
12510         };
12511         var body;
12512         // branched to use + in gecko and [].join() in others
12513         if(Roo.isGecko){
12514             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12515                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12516                     "';};};";
12517         }else{
12518             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12519             body.push(tpl.body.replace(/(\r\n|\n)/g,
12520                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12521             body.push("'].join('');};};");
12522             body = body.join('');
12523         }
12524         
12525         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12526        
12527         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12528         eval(body);
12529         
12530         return this;
12531     },
12532      
12533     /**
12534      * same as applyTemplate, except it's done to one of the subTemplates
12535      * when using named templates, you can do:
12536      *
12537      * var str = pl.applySubTemplate('your-name', values);
12538      *
12539      * 
12540      * @param {Number} id of the template
12541      * @param {Object} values to apply to template
12542      * @param {Object} parent (normaly the instance of this object)
12543      */
12544     applySubTemplate : function(id, values, parent)
12545     {
12546         
12547         
12548         var t = this.tpls[id];
12549         
12550         
12551         try { 
12552             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12553                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12554                 return '';
12555             }
12556         } catch(e) {
12557             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12558             Roo.log(values);
12559           
12560             return '';
12561         }
12562         try { 
12563             
12564             if(t.execCall && t.execCall.call(this, values, parent)){
12565                 return '';
12566             }
12567         } catch(e) {
12568             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12569             Roo.log(values);
12570             return '';
12571         }
12572         
12573         try {
12574             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12575             parent = t.target ? values : parent;
12576             if(t.forCall && vs instanceof Array){
12577                 var buf = [];
12578                 for(var i = 0, len = vs.length; i < len; i++){
12579                     try {
12580                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12581                     } catch (e) {
12582                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12583                         Roo.log(e.body);
12584                         //Roo.log(t.compiled);
12585                         Roo.log(vs[i]);
12586                     }   
12587                 }
12588                 return buf.join('');
12589             }
12590         } catch (e) {
12591             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12592             Roo.log(values);
12593             return '';
12594         }
12595         try {
12596             return t.compiled.call(this, vs, parent);
12597         } catch (e) {
12598             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12599             Roo.log(e.body);
12600             //Roo.log(t.compiled);
12601             Roo.log(values);
12602             return '';
12603         }
12604     },
12605
12606    
12607
12608     applyTemplate : function(values){
12609         return this.master.compiled.call(this, values, {});
12610         //var s = this.subs;
12611     },
12612
12613     apply : function(){
12614         return this.applyTemplate.apply(this, arguments);
12615     }
12616
12617  });
12618
12619 Roo.DomTemplate.from = function(el){
12620     el = Roo.getDom(el);
12621     return new Roo.Domtemplate(el.value || el.innerHTML);
12622 };/*
12623  * Based on:
12624  * Ext JS Library 1.1.1
12625  * Copyright(c) 2006-2007, Ext JS, LLC.
12626  *
12627  * Originally Released Under LGPL - original licence link has changed is not relivant.
12628  *
12629  * Fork - LGPL
12630  * <script type="text/javascript">
12631  */
12632
12633 /**
12634  * @class Roo.util.DelayedTask
12635  * Provides a convenient method of performing setTimeout where a new
12636  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12637  * You can use this class to buffer
12638  * the keypress events for a certain number of milliseconds, and perform only if they stop
12639  * for that amount of time.
12640  * @constructor The parameters to this constructor serve as defaults and are not required.
12641  * @param {Function} fn (optional) The default function to timeout
12642  * @param {Object} scope (optional) The default scope of that timeout
12643  * @param {Array} args (optional) The default Array of arguments
12644  */
12645 Roo.util.DelayedTask = function(fn, scope, args){
12646     var id = null, d, t;
12647
12648     var call = function(){
12649         var now = new Date().getTime();
12650         if(now - t >= d){
12651             clearInterval(id);
12652             id = null;
12653             fn.apply(scope, args || []);
12654         }
12655     };
12656     /**
12657      * Cancels any pending timeout and queues a new one
12658      * @param {Number} delay The milliseconds to delay
12659      * @param {Function} newFn (optional) Overrides function passed to constructor
12660      * @param {Object} newScope (optional) Overrides scope passed to constructor
12661      * @param {Array} newArgs (optional) Overrides args passed to constructor
12662      */
12663     this.delay = function(delay, newFn, newScope, newArgs){
12664         if(id && delay != d){
12665             this.cancel();
12666         }
12667         d = delay;
12668         t = new Date().getTime();
12669         fn = newFn || fn;
12670         scope = newScope || scope;
12671         args = newArgs || args;
12672         if(!id){
12673             id = setInterval(call, d);
12674         }
12675     };
12676
12677     /**
12678      * Cancel the last queued timeout
12679      */
12680     this.cancel = function(){
12681         if(id){
12682             clearInterval(id);
12683             id = null;
12684         }
12685     };
12686 };/*
12687  * Based on:
12688  * Ext JS Library 1.1.1
12689  * Copyright(c) 2006-2007, Ext JS, LLC.
12690  *
12691  * Originally Released Under LGPL - original licence link has changed is not relivant.
12692  *
12693  * Fork - LGPL
12694  * <script type="text/javascript">
12695  */
12696  
12697  
12698 Roo.util.TaskRunner = function(interval){
12699     interval = interval || 10;
12700     var tasks = [], removeQueue = [];
12701     var id = 0;
12702     var running = false;
12703
12704     var stopThread = function(){
12705         running = false;
12706         clearInterval(id);
12707         id = 0;
12708     };
12709
12710     var startThread = function(){
12711         if(!running){
12712             running = true;
12713             id = setInterval(runTasks, interval);
12714         }
12715     };
12716
12717     var removeTask = function(task){
12718         removeQueue.push(task);
12719         if(task.onStop){
12720             task.onStop();
12721         }
12722     };
12723
12724     var runTasks = function(){
12725         if(removeQueue.length > 0){
12726             for(var i = 0, len = removeQueue.length; i < len; i++){
12727                 tasks.remove(removeQueue[i]);
12728             }
12729             removeQueue = [];
12730             if(tasks.length < 1){
12731                 stopThread();
12732                 return;
12733             }
12734         }
12735         var now = new Date().getTime();
12736         for(var i = 0, len = tasks.length; i < len; ++i){
12737             var t = tasks[i];
12738             var itime = now - t.taskRunTime;
12739             if(t.interval <= itime){
12740                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12741                 t.taskRunTime = now;
12742                 if(rt === false || t.taskRunCount === t.repeat){
12743                     removeTask(t);
12744                     return;
12745                 }
12746             }
12747             if(t.duration && t.duration <= (now - t.taskStartTime)){
12748                 removeTask(t);
12749             }
12750         }
12751     };
12752
12753     /**
12754      * Queues a new task.
12755      * @param {Object} task
12756      */
12757     this.start = function(task){
12758         tasks.push(task);
12759         task.taskStartTime = new Date().getTime();
12760         task.taskRunTime = 0;
12761         task.taskRunCount = 0;
12762         startThread();
12763         return task;
12764     };
12765
12766     this.stop = function(task){
12767         removeTask(task);
12768         return task;
12769     };
12770
12771     this.stopAll = function(){
12772         stopThread();
12773         for(var i = 0, len = tasks.length; i < len; i++){
12774             if(tasks[i].onStop){
12775                 tasks[i].onStop();
12776             }
12777         }
12778         tasks = [];
12779         removeQueue = [];
12780     };
12781 };
12782
12783 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12784  * Based on:
12785  * Ext JS Library 1.1.1
12786  * Copyright(c) 2006-2007, Ext JS, LLC.
12787  *
12788  * Originally Released Under LGPL - original licence link has changed is not relivant.
12789  *
12790  * Fork - LGPL
12791  * <script type="text/javascript">
12792  */
12793
12794  
12795 /**
12796  * @class Roo.util.MixedCollection
12797  * @extends Roo.util.Observable
12798  * A Collection class that maintains both numeric indexes and keys and exposes events.
12799  * @constructor
12800  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12801  * collection (defaults to false)
12802  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12803  * and return the key value for that item.  This is used when available to look up the key on items that
12804  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12805  * equivalent to providing an implementation for the {@link #getKey} method.
12806  */
12807 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12808     this.items = [];
12809     this.map = {};
12810     this.keys = [];
12811     this.length = 0;
12812     this.addEvents({
12813         /**
12814          * @event clear
12815          * Fires when the collection is cleared.
12816          */
12817         "clear" : true,
12818         /**
12819          * @event add
12820          * Fires when an item is added to the collection.
12821          * @param {Number} index The index at which the item was added.
12822          * @param {Object} o The item added.
12823          * @param {String} key The key associated with the added item.
12824          */
12825         "add" : true,
12826         /**
12827          * @event replace
12828          * Fires when an item is replaced in the collection.
12829          * @param {String} key he key associated with the new added.
12830          * @param {Object} old The item being replaced.
12831          * @param {Object} new The new item.
12832          */
12833         "replace" : true,
12834         /**
12835          * @event remove
12836          * Fires when an item is removed from the collection.
12837          * @param {Object} o The item being removed.
12838          * @param {String} key (optional) The key associated with the removed item.
12839          */
12840         "remove" : true,
12841         "sort" : true
12842     });
12843     this.allowFunctions = allowFunctions === true;
12844     if(keyFn){
12845         this.getKey = keyFn;
12846     }
12847     Roo.util.MixedCollection.superclass.constructor.call(this);
12848 };
12849
12850 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12851     allowFunctions : false,
12852     
12853 /**
12854  * Adds an item to the collection.
12855  * @param {String} key The key to associate with the item
12856  * @param {Object} o The item to add.
12857  * @return {Object} The item added.
12858  */
12859     add : function(key, o){
12860         if(arguments.length == 1){
12861             o = arguments[0];
12862             key = this.getKey(o);
12863         }
12864         if(typeof key == "undefined" || key === null){
12865             this.length++;
12866             this.items.push(o);
12867             this.keys.push(null);
12868         }else{
12869             var old = this.map[key];
12870             if(old){
12871                 return this.replace(key, o);
12872             }
12873             this.length++;
12874             this.items.push(o);
12875             this.map[key] = o;
12876             this.keys.push(key);
12877         }
12878         this.fireEvent("add", this.length-1, o, key);
12879         return o;
12880     },
12881        
12882 /**
12883   * MixedCollection has a generic way to fetch keys if you implement getKey.
12884 <pre><code>
12885 // normal way
12886 var mc = new Roo.util.MixedCollection();
12887 mc.add(someEl.dom.id, someEl);
12888 mc.add(otherEl.dom.id, otherEl);
12889 //and so on
12890
12891 // using getKey
12892 var mc = new Roo.util.MixedCollection();
12893 mc.getKey = function(el){
12894    return el.dom.id;
12895 };
12896 mc.add(someEl);
12897 mc.add(otherEl);
12898
12899 // or via the constructor
12900 var mc = new Roo.util.MixedCollection(false, function(el){
12901    return el.dom.id;
12902 });
12903 mc.add(someEl);
12904 mc.add(otherEl);
12905 </code></pre>
12906  * @param o {Object} The item for which to find the key.
12907  * @return {Object} The key for the passed item.
12908  */
12909     getKey : function(o){
12910          return o.id; 
12911     },
12912    
12913 /**
12914  * Replaces an item in the collection.
12915  * @param {String} key The key associated with the item to replace, or the item to replace.
12916  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12917  * @return {Object}  The new item.
12918  */
12919     replace : function(key, o){
12920         if(arguments.length == 1){
12921             o = arguments[0];
12922             key = this.getKey(o);
12923         }
12924         var old = this.item(key);
12925         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12926              return this.add(key, o);
12927         }
12928         var index = this.indexOfKey(key);
12929         this.items[index] = o;
12930         this.map[key] = o;
12931         this.fireEvent("replace", key, old, o);
12932         return o;
12933     },
12934    
12935 /**
12936  * Adds all elements of an Array or an Object to the collection.
12937  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12938  * an Array of values, each of which are added to the collection.
12939  */
12940     addAll : function(objs){
12941         if(arguments.length > 1 || objs instanceof Array){
12942             var args = arguments.length > 1 ? arguments : objs;
12943             for(var i = 0, len = args.length; i < len; i++){
12944                 this.add(args[i]);
12945             }
12946         }else{
12947             for(var key in objs){
12948                 if(this.allowFunctions || typeof objs[key] != "function"){
12949                     this.add(key, objs[key]);
12950                 }
12951             }
12952         }
12953     },
12954    
12955 /**
12956  * Executes the specified function once for every item in the collection, passing each
12957  * item as the first and only parameter. returning false from the function will stop the iteration.
12958  * @param {Function} fn The function to execute for each item.
12959  * @param {Object} scope (optional) The scope in which to execute the function.
12960  */
12961     each : function(fn, scope){
12962         var items = [].concat(this.items); // each safe for removal
12963         for(var i = 0, len = items.length; i < len; i++){
12964             if(fn.call(scope || items[i], items[i], i, len) === false){
12965                 break;
12966             }
12967         }
12968     },
12969    
12970 /**
12971  * Executes the specified function once for every key in the collection, passing each
12972  * key, and its associated item as the first two parameters.
12973  * @param {Function} fn The function to execute for each item.
12974  * @param {Object} scope (optional) The scope in which to execute the function.
12975  */
12976     eachKey : function(fn, scope){
12977         for(var i = 0, len = this.keys.length; i < len; i++){
12978             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12979         }
12980     },
12981    
12982 /**
12983  * Returns the first item in the collection which elicits a true return value from the
12984  * passed selection function.
12985  * @param {Function} fn The selection function to execute for each item.
12986  * @param {Object} scope (optional) The scope in which to execute the function.
12987  * @return {Object} The first item in the collection which returned true from the selection function.
12988  */
12989     find : function(fn, scope){
12990         for(var i = 0, len = this.items.length; i < len; i++){
12991             if(fn.call(scope || window, this.items[i], this.keys[i])){
12992                 return this.items[i];
12993             }
12994         }
12995         return null;
12996     },
12997    
12998 /**
12999  * Inserts an item at the specified index in the collection.
13000  * @param {Number} index The index to insert the item at.
13001  * @param {String} key The key to associate with the new item, or the item itself.
13002  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13003  * @return {Object} The item inserted.
13004  */
13005     insert : function(index, key, o){
13006         if(arguments.length == 2){
13007             o = arguments[1];
13008             key = this.getKey(o);
13009         }
13010         if(index >= this.length){
13011             return this.add(key, o);
13012         }
13013         this.length++;
13014         this.items.splice(index, 0, o);
13015         if(typeof key != "undefined" && key != null){
13016             this.map[key] = o;
13017         }
13018         this.keys.splice(index, 0, key);
13019         this.fireEvent("add", index, o, key);
13020         return o;
13021     },
13022    
13023 /**
13024  * Removed an item from the collection.
13025  * @param {Object} o The item to remove.
13026  * @return {Object} The item removed.
13027  */
13028     remove : function(o){
13029         return this.removeAt(this.indexOf(o));
13030     },
13031    
13032 /**
13033  * Remove an item from a specified index in the collection.
13034  * @param {Number} index The index within the collection of the item to remove.
13035  */
13036     removeAt : function(index){
13037         if(index < this.length && index >= 0){
13038             this.length--;
13039             var o = this.items[index];
13040             this.items.splice(index, 1);
13041             var key = this.keys[index];
13042             if(typeof key != "undefined"){
13043                 delete this.map[key];
13044             }
13045             this.keys.splice(index, 1);
13046             this.fireEvent("remove", o, key);
13047         }
13048     },
13049    
13050 /**
13051  * Removed an item associated with the passed key fom the collection.
13052  * @param {String} key The key of the item to remove.
13053  */
13054     removeKey : function(key){
13055         return this.removeAt(this.indexOfKey(key));
13056     },
13057    
13058 /**
13059  * Returns the number of items in the collection.
13060  * @return {Number} the number of items in the collection.
13061  */
13062     getCount : function(){
13063         return this.length; 
13064     },
13065    
13066 /**
13067  * Returns index within the collection of the passed Object.
13068  * @param {Object} o The item to find the index of.
13069  * @return {Number} index of the item.
13070  */
13071     indexOf : function(o){
13072         if(!this.items.indexOf){
13073             for(var i = 0, len = this.items.length; i < len; i++){
13074                 if(this.items[i] == o) return i;
13075             }
13076             return -1;
13077         }else{
13078             return this.items.indexOf(o);
13079         }
13080     },
13081    
13082 /**
13083  * Returns index within the collection of the passed key.
13084  * @param {String} key The key to find the index of.
13085  * @return {Number} index of the key.
13086  */
13087     indexOfKey : function(key){
13088         if(!this.keys.indexOf){
13089             for(var i = 0, len = this.keys.length; i < len; i++){
13090                 if(this.keys[i] == key) return i;
13091             }
13092             return -1;
13093         }else{
13094             return this.keys.indexOf(key);
13095         }
13096     },
13097    
13098 /**
13099  * Returns the item associated with the passed key OR index. Key has priority over index.
13100  * @param {String/Number} key The key or index of the item.
13101  * @return {Object} The item associated with the passed key.
13102  */
13103     item : function(key){
13104         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13105         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13106     },
13107     
13108 /**
13109  * Returns the item at the specified index.
13110  * @param {Number} index The index of the item.
13111  * @return {Object}
13112  */
13113     itemAt : function(index){
13114         return this.items[index];
13115     },
13116     
13117 /**
13118  * Returns the item associated with the passed key.
13119  * @param {String/Number} key The key of the item.
13120  * @return {Object} The item associated with the passed key.
13121  */
13122     key : function(key){
13123         return this.map[key];
13124     },
13125    
13126 /**
13127  * Returns true if the collection contains the passed Object as an item.
13128  * @param {Object} o  The Object to look for in the collection.
13129  * @return {Boolean} True if the collection contains the Object as an item.
13130  */
13131     contains : function(o){
13132         return this.indexOf(o) != -1;
13133     },
13134    
13135 /**
13136  * Returns true if the collection contains the passed Object as a key.
13137  * @param {String} key The key to look for in the collection.
13138  * @return {Boolean} True if the collection contains the Object as a key.
13139  */
13140     containsKey : function(key){
13141         return typeof this.map[key] != "undefined";
13142     },
13143    
13144 /**
13145  * Removes all items from the collection.
13146  */
13147     clear : function(){
13148         this.length = 0;
13149         this.items = [];
13150         this.keys = [];
13151         this.map = {};
13152         this.fireEvent("clear");
13153     },
13154    
13155 /**
13156  * Returns the first item in the collection.
13157  * @return {Object} the first item in the collection..
13158  */
13159     first : function(){
13160         return this.items[0]; 
13161     },
13162    
13163 /**
13164  * Returns the last item in the collection.
13165  * @return {Object} the last item in the collection..
13166  */
13167     last : function(){
13168         return this.items[this.length-1];   
13169     },
13170     
13171     _sort : function(property, dir, fn){
13172         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13173         fn = fn || function(a, b){
13174             return a-b;
13175         };
13176         var c = [], k = this.keys, items = this.items;
13177         for(var i = 0, len = items.length; i < len; i++){
13178             c[c.length] = {key: k[i], value: items[i], index: i};
13179         }
13180         c.sort(function(a, b){
13181             var v = fn(a[property], b[property]) * dsc;
13182             if(v == 0){
13183                 v = (a.index < b.index ? -1 : 1);
13184             }
13185             return v;
13186         });
13187         for(var i = 0, len = c.length; i < len; i++){
13188             items[i] = c[i].value;
13189             k[i] = c[i].key;
13190         }
13191         this.fireEvent("sort", this);
13192     },
13193     
13194     /**
13195      * Sorts this collection with the passed comparison function
13196      * @param {String} direction (optional) "ASC" or "DESC"
13197      * @param {Function} fn (optional) comparison function
13198      */
13199     sort : function(dir, fn){
13200         this._sort("value", dir, fn);
13201     },
13202     
13203     /**
13204      * Sorts this collection by keys
13205      * @param {String} direction (optional) "ASC" or "DESC"
13206      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13207      */
13208     keySort : function(dir, fn){
13209         this._sort("key", dir, fn || function(a, b){
13210             return String(a).toUpperCase()-String(b).toUpperCase();
13211         });
13212     },
13213     
13214     /**
13215      * Returns a range of items in this collection
13216      * @param {Number} startIndex (optional) defaults to 0
13217      * @param {Number} endIndex (optional) default to the last item
13218      * @return {Array} An array of items
13219      */
13220     getRange : function(start, end){
13221         var items = this.items;
13222         if(items.length < 1){
13223             return [];
13224         }
13225         start = start || 0;
13226         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13227         var r = [];
13228         if(start <= end){
13229             for(var i = start; i <= end; i++) {
13230                     r[r.length] = items[i];
13231             }
13232         }else{
13233             for(var i = start; i >= end; i--) {
13234                     r[r.length] = items[i];
13235             }
13236         }
13237         return r;
13238     },
13239         
13240     /**
13241      * Filter the <i>objects</i> in this collection by a specific property. 
13242      * Returns a new collection that has been filtered.
13243      * @param {String} property A property on your objects
13244      * @param {String/RegExp} value Either string that the property values 
13245      * should start with or a RegExp to test against the property
13246      * @return {MixedCollection} The new filtered collection
13247      */
13248     filter : function(property, value){
13249         if(!value.exec){ // not a regex
13250             value = String(value);
13251             if(value.length == 0){
13252                 return this.clone();
13253             }
13254             value = new RegExp("^" + Roo.escapeRe(value), "i");
13255         }
13256         return this.filterBy(function(o){
13257             return o && value.test(o[property]);
13258         });
13259         },
13260     
13261     /**
13262      * Filter by a function. * Returns a new collection that has been filtered.
13263      * The passed function will be called with each 
13264      * object in the collection. If the function returns true, the value is included 
13265      * otherwise it is filtered.
13266      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13267      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13268      * @return {MixedCollection} The new filtered collection
13269      */
13270     filterBy : function(fn, scope){
13271         var r = new Roo.util.MixedCollection();
13272         r.getKey = this.getKey;
13273         var k = this.keys, it = this.items;
13274         for(var i = 0, len = it.length; i < len; i++){
13275             if(fn.call(scope||this, it[i], k[i])){
13276                                 r.add(k[i], it[i]);
13277                         }
13278         }
13279         return r;
13280     },
13281     
13282     /**
13283      * Creates a duplicate of this collection
13284      * @return {MixedCollection}
13285      */
13286     clone : function(){
13287         var r = new Roo.util.MixedCollection();
13288         var k = this.keys, it = this.items;
13289         for(var i = 0, len = it.length; i < len; i++){
13290             r.add(k[i], it[i]);
13291         }
13292         r.getKey = this.getKey;
13293         return r;
13294     }
13295 });
13296 /**
13297  * Returns the item associated with the passed key or index.
13298  * @method
13299  * @param {String/Number} key The key or index of the item.
13300  * @return {Object} The item associated with the passed key.
13301  */
13302 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13303  * Based on:
13304  * Ext JS Library 1.1.1
13305  * Copyright(c) 2006-2007, Ext JS, LLC.
13306  *
13307  * Originally Released Under LGPL - original licence link has changed is not relivant.
13308  *
13309  * Fork - LGPL
13310  * <script type="text/javascript">
13311  */
13312 /**
13313  * @class Roo.util.JSON
13314  * Modified version of Douglas Crockford"s json.js that doesn"t
13315  * mess with the Object prototype 
13316  * http://www.json.org/js.html
13317  * @singleton
13318  */
13319 Roo.util.JSON = new (function(){
13320     var useHasOwn = {}.hasOwnProperty ? true : false;
13321     
13322     // crashes Safari in some instances
13323     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13324     
13325     var pad = function(n) {
13326         return n < 10 ? "0" + n : n;
13327     };
13328     
13329     var m = {
13330         "\b": '\\b',
13331         "\t": '\\t',
13332         "\n": '\\n',
13333         "\f": '\\f',
13334         "\r": '\\r',
13335         '"' : '\\"',
13336         "\\": '\\\\'
13337     };
13338
13339     var encodeString = function(s){
13340         if (/["\\\x00-\x1f]/.test(s)) {
13341             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13342                 var c = m[b];
13343                 if(c){
13344                     return c;
13345                 }
13346                 c = b.charCodeAt();
13347                 return "\\u00" +
13348                     Math.floor(c / 16).toString(16) +
13349                     (c % 16).toString(16);
13350             }) + '"';
13351         }
13352         return '"' + s + '"';
13353     };
13354     
13355     var encodeArray = function(o){
13356         var a = ["["], b, i, l = o.length, v;
13357             for (i = 0; i < l; i += 1) {
13358                 v = o[i];
13359                 switch (typeof v) {
13360                     case "undefined":
13361                     case "function":
13362                     case "unknown":
13363                         break;
13364                     default:
13365                         if (b) {
13366                             a.push(',');
13367                         }
13368                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13369                         b = true;
13370                 }
13371             }
13372             a.push("]");
13373             return a.join("");
13374     };
13375     
13376     var encodeDate = function(o){
13377         return '"' + o.getFullYear() + "-" +
13378                 pad(o.getMonth() + 1) + "-" +
13379                 pad(o.getDate()) + "T" +
13380                 pad(o.getHours()) + ":" +
13381                 pad(o.getMinutes()) + ":" +
13382                 pad(o.getSeconds()) + '"';
13383     };
13384     
13385     /**
13386      * Encodes an Object, Array or other value
13387      * @param {Mixed} o The variable to encode
13388      * @return {String} The JSON string
13389      */
13390     this.encode = function(o)
13391     {
13392         // should this be extended to fully wrap stringify..
13393         
13394         if(typeof o == "undefined" || o === null){
13395             return "null";
13396         }else if(o instanceof Array){
13397             return encodeArray(o);
13398         }else if(o instanceof Date){
13399             return encodeDate(o);
13400         }else if(typeof o == "string"){
13401             return encodeString(o);
13402         }else if(typeof o == "number"){
13403             return isFinite(o) ? String(o) : "null";
13404         }else if(typeof o == "boolean"){
13405             return String(o);
13406         }else {
13407             var a = ["{"], b, i, v;
13408             for (i in o) {
13409                 if(!useHasOwn || o.hasOwnProperty(i)) {
13410                     v = o[i];
13411                     switch (typeof v) {
13412                     case "undefined":
13413                     case "function":
13414                     case "unknown":
13415                         break;
13416                     default:
13417                         if(b){
13418                             a.push(',');
13419                         }
13420                         a.push(this.encode(i), ":",
13421                                 v === null ? "null" : this.encode(v));
13422                         b = true;
13423                     }
13424                 }
13425             }
13426             a.push("}");
13427             return a.join("");
13428         }
13429     };
13430     
13431     /**
13432      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13433      * @param {String} json The JSON string
13434      * @return {Object} The resulting object
13435      */
13436     this.decode = function(json){
13437         
13438         return  /** eval:var:json */ eval("(" + json + ')');
13439     };
13440 })();
13441 /** 
13442  * Shorthand for {@link Roo.util.JSON#encode}
13443  * @member Roo encode 
13444  * @method */
13445 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13446 /** 
13447  * Shorthand for {@link Roo.util.JSON#decode}
13448  * @member Roo decode 
13449  * @method */
13450 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13451 /*
13452  * Based on:
13453  * Ext JS Library 1.1.1
13454  * Copyright(c) 2006-2007, Ext JS, LLC.
13455  *
13456  * Originally Released Under LGPL - original licence link has changed is not relivant.
13457  *
13458  * Fork - LGPL
13459  * <script type="text/javascript">
13460  */
13461  
13462 /**
13463  * @class Roo.util.Format
13464  * Reusable data formatting functions
13465  * @singleton
13466  */
13467 Roo.util.Format = function(){
13468     var trimRe = /^\s+|\s+$/g;
13469     return {
13470         /**
13471          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13472          * @param {String} value The string to truncate
13473          * @param {Number} length The maximum length to allow before truncating
13474          * @return {String} The converted text
13475          */
13476         ellipsis : function(value, len){
13477             if(value && value.length > len){
13478                 return value.substr(0, len-3)+"...";
13479             }
13480             return value;
13481         },
13482
13483         /**
13484          * Checks a reference and converts it to empty string if it is undefined
13485          * @param {Mixed} value Reference to check
13486          * @return {Mixed} Empty string if converted, otherwise the original value
13487          */
13488         undef : function(value){
13489             return typeof value != "undefined" ? value : "";
13490         },
13491
13492         /**
13493          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13494          * @param {String} value The string to encode
13495          * @return {String} The encoded text
13496          */
13497         htmlEncode : function(value){
13498             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13499         },
13500
13501         /**
13502          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13503          * @param {String} value The string to decode
13504          * @return {String} The decoded text
13505          */
13506         htmlDecode : function(value){
13507             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13508         },
13509
13510         /**
13511          * Trims any whitespace from either side of a string
13512          * @param {String} value The text to trim
13513          * @return {String} The trimmed text
13514          */
13515         trim : function(value){
13516             return String(value).replace(trimRe, "");
13517         },
13518
13519         /**
13520          * Returns a substring from within an original string
13521          * @param {String} value The original text
13522          * @param {Number} start The start index of the substring
13523          * @param {Number} length The length of the substring
13524          * @return {String} The substring
13525          */
13526         substr : function(value, start, length){
13527             return String(value).substr(start, length);
13528         },
13529
13530         /**
13531          * Converts a string to all lower case letters
13532          * @param {String} value The text to convert
13533          * @return {String} The converted text
13534          */
13535         lowercase : function(value){
13536             return String(value).toLowerCase();
13537         },
13538
13539         /**
13540          * Converts a string to all upper case letters
13541          * @param {String} value The text to convert
13542          * @return {String} The converted text
13543          */
13544         uppercase : function(value){
13545             return String(value).toUpperCase();
13546         },
13547
13548         /**
13549          * Converts the first character only of a string to upper case
13550          * @param {String} value The text to convert
13551          * @return {String} The converted text
13552          */
13553         capitalize : function(value){
13554             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13555         },
13556
13557         // private
13558         call : function(value, fn){
13559             if(arguments.length > 2){
13560                 var args = Array.prototype.slice.call(arguments, 2);
13561                 args.unshift(value);
13562                  
13563                 return /** eval:var:value */  eval(fn).apply(window, args);
13564             }else{
13565                 /** eval:var:value */
13566                 return /** eval:var:value */ eval(fn).call(window, value);
13567             }
13568         },
13569
13570        
13571         /**
13572          * safer version of Math.toFixed..??/
13573          * @param {Number/String} value The numeric value to format
13574          * @param {Number/String} value Decimal places 
13575          * @return {String} The formatted currency string
13576          */
13577         toFixed : function(v, n)
13578         {
13579             // why not use to fixed - precision is buggered???
13580             if (!n) {
13581                 return Math.round(v-0);
13582             }
13583             var fact = Math.pow(10,n+1);
13584             v = (Math.round((v-0)*fact))/fact;
13585             var z = (''+fact).substring(2);
13586             if (v == Math.floor(v)) {
13587                 return Math.floor(v) + '.' + z;
13588             }
13589             
13590             // now just padd decimals..
13591             var ps = String(v).split('.');
13592             var fd = (ps[1] + z);
13593             var r = fd.substring(0,n); 
13594             var rm = fd.substring(n); 
13595             if (rm < 5) {
13596                 return ps[0] + '.' + r;
13597             }
13598             r*=1; // turn it into a number;
13599             r++;
13600             if (String(r).length != n) {
13601                 ps[0]*=1;
13602                 ps[0]++;
13603                 r = String(r).substring(1); // chop the end off.
13604             }
13605             
13606             return ps[0] + '.' + r;
13607              
13608         },
13609         
13610         /**
13611          * Format a number as US currency
13612          * @param {Number/String} value The numeric value to format
13613          * @return {String} The formatted currency string
13614          */
13615         usMoney : function(v){
13616             return '$' + Roo.util.Format.number(v);
13617         },
13618         
13619         /**
13620          * Format a number
13621          * eventually this should probably emulate php's number_format
13622          * @param {Number/String} value The numeric value to format
13623          * @param {Number} decimals number of decimal places
13624          * @return {String} The formatted currency string
13625          */
13626         number : function(v,decimals)
13627         {
13628             // multiply and round.
13629             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13630             var mul = Math.pow(10, decimals);
13631             var zero = String(mul).substring(1);
13632             v = (Math.round((v-0)*mul))/mul;
13633             
13634             // if it's '0' number.. then
13635             
13636             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13637             v = String(v);
13638             var ps = v.split('.');
13639             var whole = ps[0];
13640             
13641             
13642             var r = /(\d+)(\d{3})/;
13643             // add comma's
13644             while (r.test(whole)) {
13645                 whole = whole.replace(r, '$1' + ',' + '$2');
13646             }
13647             
13648             
13649             var sub = ps[1] ?
13650                     // has decimals..
13651                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13652                     // does not have decimals
13653                     (decimals ? ('.' + zero) : '');
13654             
13655             
13656             return whole + sub ;
13657         },
13658         
13659         /**
13660          * Parse a value into a formatted date using the specified format pattern.
13661          * @param {Mixed} value The value to format
13662          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13663          * @return {String} The formatted date string
13664          */
13665         date : function(v, format){
13666             if(!v){
13667                 return "";
13668             }
13669             if(!(v instanceof Date)){
13670                 v = new Date(Date.parse(v));
13671             }
13672             return v.dateFormat(format || Roo.util.Format.defaults.date);
13673         },
13674
13675         /**
13676          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13677          * @param {String} format Any valid date format string
13678          * @return {Function} The date formatting function
13679          */
13680         dateRenderer : function(format){
13681             return function(v){
13682                 return Roo.util.Format.date(v, format);  
13683             };
13684         },
13685
13686         // private
13687         stripTagsRE : /<\/?[^>]+>/gi,
13688         
13689         /**
13690          * Strips all HTML tags
13691          * @param {Mixed} value The text from which to strip tags
13692          * @return {String} The stripped text
13693          */
13694         stripTags : function(v){
13695             return !v ? v : String(v).replace(this.stripTagsRE, "");
13696         }
13697     };
13698 }();
13699 Roo.util.Format.defaults = {
13700     date : 'd/M/Y'
13701 };/*
13702  * Based on:
13703  * Ext JS Library 1.1.1
13704  * Copyright(c) 2006-2007, Ext JS, LLC.
13705  *
13706  * Originally Released Under LGPL - original licence link has changed is not relivant.
13707  *
13708  * Fork - LGPL
13709  * <script type="text/javascript">
13710  */
13711
13712
13713  
13714
13715 /**
13716  * @class Roo.MasterTemplate
13717  * @extends Roo.Template
13718  * Provides a template that can have child templates. The syntax is:
13719 <pre><code>
13720 var t = new Roo.MasterTemplate(
13721         '&lt;select name="{name}"&gt;',
13722                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13723         '&lt;/select&gt;'
13724 );
13725 t.add('options', {value: 'foo', text: 'bar'});
13726 // or you can add multiple child elements in one shot
13727 t.addAll('options', [
13728     {value: 'foo', text: 'bar'},
13729     {value: 'foo2', text: 'bar2'},
13730     {value: 'foo3', text: 'bar3'}
13731 ]);
13732 // then append, applying the master template values
13733 t.append('my-form', {name: 'my-select'});
13734 </code></pre>
13735 * A name attribute for the child template is not required if you have only one child
13736 * template or you want to refer to them by index.
13737  */
13738 Roo.MasterTemplate = function(){
13739     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13740     this.originalHtml = this.html;
13741     var st = {};
13742     var m, re = this.subTemplateRe;
13743     re.lastIndex = 0;
13744     var subIndex = 0;
13745     while(m = re.exec(this.html)){
13746         var name = m[1], content = m[2];
13747         st[subIndex] = {
13748             name: name,
13749             index: subIndex,
13750             buffer: [],
13751             tpl : new Roo.Template(content)
13752         };
13753         if(name){
13754             st[name] = st[subIndex];
13755         }
13756         st[subIndex].tpl.compile();
13757         st[subIndex].tpl.call = this.call.createDelegate(this);
13758         subIndex++;
13759     }
13760     this.subCount = subIndex;
13761     this.subs = st;
13762 };
13763 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13764     /**
13765     * The regular expression used to match sub templates
13766     * @type RegExp
13767     * @property
13768     */
13769     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13770
13771     /**
13772      * Applies the passed values to a child template.
13773      * @param {String/Number} name (optional) The name or index of the child template
13774      * @param {Array/Object} values The values to be applied to the template
13775      * @return {MasterTemplate} this
13776      */
13777      add : function(name, values){
13778         if(arguments.length == 1){
13779             values = arguments[0];
13780             name = 0;
13781         }
13782         var s = this.subs[name];
13783         s.buffer[s.buffer.length] = s.tpl.apply(values);
13784         return this;
13785     },
13786
13787     /**
13788      * Applies all the passed values to a child template.
13789      * @param {String/Number} name (optional) The name or index of the child template
13790      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13791      * @param {Boolean} reset (optional) True to reset the template first
13792      * @return {MasterTemplate} this
13793      */
13794     fill : function(name, values, reset){
13795         var a = arguments;
13796         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13797             values = a[0];
13798             name = 0;
13799             reset = a[1];
13800         }
13801         if(reset){
13802             this.reset();
13803         }
13804         for(var i = 0, len = values.length; i < len; i++){
13805             this.add(name, values[i]);
13806         }
13807         return this;
13808     },
13809
13810     /**
13811      * Resets the template for reuse
13812      * @return {MasterTemplate} this
13813      */
13814      reset : function(){
13815         var s = this.subs;
13816         for(var i = 0; i < this.subCount; i++){
13817             s[i].buffer = [];
13818         }
13819         return this;
13820     },
13821
13822     applyTemplate : function(values){
13823         var s = this.subs;
13824         var replaceIndex = -1;
13825         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13826             return s[++replaceIndex].buffer.join("");
13827         });
13828         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13829     },
13830
13831     apply : function(){
13832         return this.applyTemplate.apply(this, arguments);
13833     },
13834
13835     compile : function(){return this;}
13836 });
13837
13838 /**
13839  * Alias for fill().
13840  * @method
13841  */
13842 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13843  /**
13844  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13845  * var tpl = Roo.MasterTemplate.from('element-id');
13846  * @param {String/HTMLElement} el
13847  * @param {Object} config
13848  * @static
13849  */
13850 Roo.MasterTemplate.from = function(el, config){
13851     el = Roo.getDom(el);
13852     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13853 };/*
13854  * Based on:
13855  * Ext JS Library 1.1.1
13856  * Copyright(c) 2006-2007, Ext JS, LLC.
13857  *
13858  * Originally Released Under LGPL - original licence link has changed is not relivant.
13859  *
13860  * Fork - LGPL
13861  * <script type="text/javascript">
13862  */
13863
13864  
13865 /**
13866  * @class Roo.util.CSS
13867  * Utility class for manipulating CSS rules
13868  * @singleton
13869  */
13870 Roo.util.CSS = function(){
13871         var rules = null;
13872         var doc = document;
13873
13874     var camelRe = /(-[a-z])/gi;
13875     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13876
13877    return {
13878    /**
13879     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13880     * tag and appended to the HEAD of the document.
13881     * @param {String|Object} cssText The text containing the css rules
13882     * @param {String} id An id to add to the stylesheet for later removal
13883     * @return {StyleSheet}
13884     */
13885     createStyleSheet : function(cssText, id){
13886         var ss;
13887         var head = doc.getElementsByTagName("head")[0];
13888         var nrules = doc.createElement("style");
13889         nrules.setAttribute("type", "text/css");
13890         if(id){
13891             nrules.setAttribute("id", id);
13892         }
13893         if (typeof(cssText) != 'string') {
13894             // support object maps..
13895             // not sure if this a good idea.. 
13896             // perhaps it should be merged with the general css handling
13897             // and handle js style props.
13898             var cssTextNew = [];
13899             for(var n in cssText) {
13900                 var citems = [];
13901                 for(var k in cssText[n]) {
13902                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13903                 }
13904                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13905                 
13906             }
13907             cssText = cssTextNew.join("\n");
13908             
13909         }
13910        
13911        
13912        if(Roo.isIE){
13913            head.appendChild(nrules);
13914            ss = nrules.styleSheet;
13915            ss.cssText = cssText;
13916        }else{
13917            try{
13918                 nrules.appendChild(doc.createTextNode(cssText));
13919            }catch(e){
13920                nrules.cssText = cssText; 
13921            }
13922            head.appendChild(nrules);
13923            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13924        }
13925        this.cacheStyleSheet(ss);
13926        return ss;
13927    },
13928
13929    /**
13930     * Removes a style or link tag by id
13931     * @param {String} id The id of the tag
13932     */
13933    removeStyleSheet : function(id){
13934        var existing = doc.getElementById(id);
13935        if(existing){
13936            existing.parentNode.removeChild(existing);
13937        }
13938    },
13939
13940    /**
13941     * Dynamically swaps an existing stylesheet reference for a new one
13942     * @param {String} id The id of an existing link tag to remove
13943     * @param {String} url The href of the new stylesheet to include
13944     */
13945    swapStyleSheet : function(id, url){
13946        this.removeStyleSheet(id);
13947        var ss = doc.createElement("link");
13948        ss.setAttribute("rel", "stylesheet");
13949        ss.setAttribute("type", "text/css");
13950        ss.setAttribute("id", id);
13951        ss.setAttribute("href", url);
13952        doc.getElementsByTagName("head")[0].appendChild(ss);
13953    },
13954    
13955    /**
13956     * Refresh the rule cache if you have dynamically added stylesheets
13957     * @return {Object} An object (hash) of rules indexed by selector
13958     */
13959    refreshCache : function(){
13960        return this.getRules(true);
13961    },
13962
13963    // private
13964    cacheStyleSheet : function(stylesheet){
13965        if(!rules){
13966            rules = {};
13967        }
13968        try{// try catch for cross domain access issue
13969            var ssRules = stylesheet.cssRules || stylesheet.rules;
13970            for(var j = ssRules.length-1; j >= 0; --j){
13971                rules[ssRules[j].selectorText] = ssRules[j];
13972            }
13973        }catch(e){}
13974    },
13975    
13976    /**
13977     * Gets all css rules for the document
13978     * @param {Boolean} refreshCache true to refresh the internal cache
13979     * @return {Object} An object (hash) of rules indexed by selector
13980     */
13981    getRules : function(refreshCache){
13982                 if(rules == null || refreshCache){
13983                         rules = {};
13984                         var ds = doc.styleSheets;
13985                         for(var i =0, len = ds.length; i < len; i++){
13986                             try{
13987                         this.cacheStyleSheet(ds[i]);
13988                     }catch(e){} 
13989                 }
13990                 }
13991                 return rules;
13992         },
13993         
13994         /**
13995     * Gets an an individual CSS rule by selector(s)
13996     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13997     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13998     * @return {CSSRule} The CSS rule or null if one is not found
13999     */
14000    getRule : function(selector, refreshCache){
14001                 var rs = this.getRules(refreshCache);
14002                 if(!(selector instanceof Array)){
14003                     return rs[selector];
14004                 }
14005                 for(var i = 0; i < selector.length; i++){
14006                         if(rs[selector[i]]){
14007                                 return rs[selector[i]];
14008                         }
14009                 }
14010                 return null;
14011         },
14012         
14013         
14014         /**
14015     * Updates a rule property
14016     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14017     * @param {String} property The css property
14018     * @param {String} value The new value for the property
14019     * @return {Boolean} true If a rule was found and updated
14020     */
14021    updateRule : function(selector, property, value){
14022                 if(!(selector instanceof Array)){
14023                         var rule = this.getRule(selector);
14024                         if(rule){
14025                                 rule.style[property.replace(camelRe, camelFn)] = value;
14026                                 return true;
14027                         }
14028                 }else{
14029                         for(var i = 0; i < selector.length; i++){
14030                                 if(this.updateRule(selector[i], property, value)){
14031                                         return true;
14032                                 }
14033                         }
14034                 }
14035                 return false;
14036         }
14037    };   
14038 }();/*
14039  * Based on:
14040  * Ext JS Library 1.1.1
14041  * Copyright(c) 2006-2007, Ext JS, LLC.
14042  *
14043  * Originally Released Under LGPL - original licence link has changed is not relivant.
14044  *
14045  * Fork - LGPL
14046  * <script type="text/javascript">
14047  */
14048
14049  
14050
14051 /**
14052  * @class Roo.util.ClickRepeater
14053  * @extends Roo.util.Observable
14054  * 
14055  * A wrapper class which can be applied to any element. Fires a "click" event while the
14056  * mouse is pressed. The interval between firings may be specified in the config but
14057  * defaults to 10 milliseconds.
14058  * 
14059  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14060  * 
14061  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14062  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14063  * Similar to an autorepeat key delay.
14064  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14065  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14066  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14067  *           "interval" and "delay" are ignored. "immediate" is honored.
14068  * @cfg {Boolean} preventDefault True to prevent the default click event
14069  * @cfg {Boolean} stopDefault True to stop the default click event
14070  * 
14071  * @history
14072  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14073  *     2007-02-02 jvs Renamed to ClickRepeater
14074  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14075  *
14076  *  @constructor
14077  * @param {String/HTMLElement/Element} el The element to listen on
14078  * @param {Object} config
14079  **/
14080 Roo.util.ClickRepeater = function(el, config)
14081 {
14082     this.el = Roo.get(el);
14083     this.el.unselectable();
14084
14085     Roo.apply(this, config);
14086
14087     this.addEvents({
14088     /**
14089      * @event mousedown
14090      * Fires when the mouse button is depressed.
14091      * @param {Roo.util.ClickRepeater} this
14092      */
14093         "mousedown" : true,
14094     /**
14095      * @event click
14096      * Fires on a specified interval during the time the element is pressed.
14097      * @param {Roo.util.ClickRepeater} this
14098      */
14099         "click" : true,
14100     /**
14101      * @event mouseup
14102      * Fires when the mouse key is released.
14103      * @param {Roo.util.ClickRepeater} this
14104      */
14105         "mouseup" : true
14106     });
14107
14108     this.el.on("mousedown", this.handleMouseDown, this);
14109     if(this.preventDefault || this.stopDefault){
14110         this.el.on("click", function(e){
14111             if(this.preventDefault){
14112                 e.preventDefault();
14113             }
14114             if(this.stopDefault){
14115                 e.stopEvent();
14116             }
14117         }, this);
14118     }
14119
14120     // allow inline handler
14121     if(this.handler){
14122         this.on("click", this.handler,  this.scope || this);
14123     }
14124
14125     Roo.util.ClickRepeater.superclass.constructor.call(this);
14126 };
14127
14128 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14129     interval : 20,
14130     delay: 250,
14131     preventDefault : true,
14132     stopDefault : false,
14133     timer : 0,
14134
14135     // private
14136     handleMouseDown : function(){
14137         clearTimeout(this.timer);
14138         this.el.blur();
14139         if(this.pressClass){
14140             this.el.addClass(this.pressClass);
14141         }
14142         this.mousedownTime = new Date();
14143
14144         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14145         this.el.on("mouseout", this.handleMouseOut, this);
14146
14147         this.fireEvent("mousedown", this);
14148         this.fireEvent("click", this);
14149         
14150         this.timer = this.click.defer(this.delay || this.interval, this);
14151     },
14152
14153     // private
14154     click : function(){
14155         this.fireEvent("click", this);
14156         this.timer = this.click.defer(this.getInterval(), this);
14157     },
14158
14159     // private
14160     getInterval: function(){
14161         if(!this.accelerate){
14162             return this.interval;
14163         }
14164         var pressTime = this.mousedownTime.getElapsed();
14165         if(pressTime < 500){
14166             return 400;
14167         }else if(pressTime < 1700){
14168             return 320;
14169         }else if(pressTime < 2600){
14170             return 250;
14171         }else if(pressTime < 3500){
14172             return 180;
14173         }else if(pressTime < 4400){
14174             return 140;
14175         }else if(pressTime < 5300){
14176             return 80;
14177         }else if(pressTime < 6200){
14178             return 50;
14179         }else{
14180             return 10;
14181         }
14182     },
14183
14184     // private
14185     handleMouseOut : function(){
14186         clearTimeout(this.timer);
14187         if(this.pressClass){
14188             this.el.removeClass(this.pressClass);
14189         }
14190         this.el.on("mouseover", this.handleMouseReturn, this);
14191     },
14192
14193     // private
14194     handleMouseReturn : function(){
14195         this.el.un("mouseover", this.handleMouseReturn);
14196         if(this.pressClass){
14197             this.el.addClass(this.pressClass);
14198         }
14199         this.click();
14200     },
14201
14202     // private
14203     handleMouseUp : function(){
14204         clearTimeout(this.timer);
14205         this.el.un("mouseover", this.handleMouseReturn);
14206         this.el.un("mouseout", this.handleMouseOut);
14207         Roo.get(document).un("mouseup", this.handleMouseUp);
14208         this.el.removeClass(this.pressClass);
14209         this.fireEvent("mouseup", this);
14210     }
14211 });/*
14212  * Based on:
14213  * Ext JS Library 1.1.1
14214  * Copyright(c) 2006-2007, Ext JS, LLC.
14215  *
14216  * Originally Released Under LGPL - original licence link has changed is not relivant.
14217  *
14218  * Fork - LGPL
14219  * <script type="text/javascript">
14220  */
14221
14222  
14223 /**
14224  * @class Roo.KeyNav
14225  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14226  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14227  * way to implement custom navigation schemes for any UI component.</p>
14228  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14229  * pageUp, pageDown, del, home, end.  Usage:</p>
14230  <pre><code>
14231 var nav = new Roo.KeyNav("my-element", {
14232     "left" : function(e){
14233         this.moveLeft(e.ctrlKey);
14234     },
14235     "right" : function(e){
14236         this.moveRight(e.ctrlKey);
14237     },
14238     "enter" : function(e){
14239         this.save();
14240     },
14241     scope : this
14242 });
14243 </code></pre>
14244  * @constructor
14245  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14246  * @param {Object} config The config
14247  */
14248 Roo.KeyNav = function(el, config){
14249     this.el = Roo.get(el);
14250     Roo.apply(this, config);
14251     if(!this.disabled){
14252         this.disabled = true;
14253         this.enable();
14254     }
14255 };
14256
14257 Roo.KeyNav.prototype = {
14258     /**
14259      * @cfg {Boolean} disabled
14260      * True to disable this KeyNav instance (defaults to false)
14261      */
14262     disabled : false,
14263     /**
14264      * @cfg {String} defaultEventAction
14265      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14266      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14267      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14268      */
14269     defaultEventAction: "stopEvent",
14270     /**
14271      * @cfg {Boolean} forceKeyDown
14272      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14273      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14274      * handle keydown instead of keypress.
14275      */
14276     forceKeyDown : false,
14277
14278     // private
14279     prepareEvent : function(e){
14280         var k = e.getKey();
14281         var h = this.keyToHandler[k];
14282         //if(h && this[h]){
14283         //    e.stopPropagation();
14284         //}
14285         if(Roo.isSafari && h && k >= 37 && k <= 40){
14286             e.stopEvent();
14287         }
14288     },
14289
14290     // private
14291     relay : function(e){
14292         var k = e.getKey();
14293         var h = this.keyToHandler[k];
14294         if(h && this[h]){
14295             if(this.doRelay(e, this[h], h) !== true){
14296                 e[this.defaultEventAction]();
14297             }
14298         }
14299     },
14300
14301     // private
14302     doRelay : function(e, h, hname){
14303         return h.call(this.scope || this, e);
14304     },
14305
14306     // possible handlers
14307     enter : false,
14308     left : false,
14309     right : false,
14310     up : false,
14311     down : false,
14312     tab : false,
14313     esc : false,
14314     pageUp : false,
14315     pageDown : false,
14316     del : false,
14317     home : false,
14318     end : false,
14319
14320     // quick lookup hash
14321     keyToHandler : {
14322         37 : "left",
14323         39 : "right",
14324         38 : "up",
14325         40 : "down",
14326         33 : "pageUp",
14327         34 : "pageDown",
14328         46 : "del",
14329         36 : "home",
14330         35 : "end",
14331         13 : "enter",
14332         27 : "esc",
14333         9  : "tab"
14334     },
14335
14336         /**
14337          * Enable this KeyNav
14338          */
14339         enable: function(){
14340                 if(this.disabled){
14341             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14342             // the EventObject will normalize Safari automatically
14343             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14344                 this.el.on("keydown", this.relay,  this);
14345             }else{
14346                 this.el.on("keydown", this.prepareEvent,  this);
14347                 this.el.on("keypress", this.relay,  this);
14348             }
14349                     this.disabled = false;
14350                 }
14351         },
14352
14353         /**
14354          * Disable this KeyNav
14355          */
14356         disable: function(){
14357                 if(!this.disabled){
14358                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14359                 this.el.un("keydown", this.relay);
14360             }else{
14361                 this.el.un("keydown", this.prepareEvent);
14362                 this.el.un("keypress", this.relay);
14363             }
14364                     this.disabled = true;
14365                 }
14366         }
14367 };/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378  
14379 /**
14380  * @class Roo.KeyMap
14381  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14382  * The constructor accepts the same config object as defined by {@link #addBinding}.
14383  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14384  * combination it will call the function with this signature (if the match is a multi-key
14385  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14386  * A KeyMap can also handle a string representation of keys.<br />
14387  * Usage:
14388  <pre><code>
14389 // map one key by key code
14390 var map = new Roo.KeyMap("my-element", {
14391     key: 13, // or Roo.EventObject.ENTER
14392     fn: myHandler,
14393     scope: myObject
14394 });
14395
14396 // map multiple keys to one action by string
14397 var map = new Roo.KeyMap("my-element", {
14398     key: "a\r\n\t",
14399     fn: myHandler,
14400     scope: myObject
14401 });
14402
14403 // map multiple keys to multiple actions by strings and array of codes
14404 var map = new Roo.KeyMap("my-element", [
14405     {
14406         key: [10,13],
14407         fn: function(){ alert("Return was pressed"); }
14408     }, {
14409         key: "abc",
14410         fn: function(){ alert('a, b or c was pressed'); }
14411     }, {
14412         key: "\t",
14413         ctrl:true,
14414         shift:true,
14415         fn: function(){ alert('Control + shift + tab was pressed.'); }
14416     }
14417 ]);
14418 </code></pre>
14419  * <b>Note: A KeyMap starts enabled</b>
14420  * @constructor
14421  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14422  * @param {Object} config The config (see {@link #addBinding})
14423  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14424  */
14425 Roo.KeyMap = function(el, config, eventName){
14426     this.el  = Roo.get(el);
14427     this.eventName = eventName || "keydown";
14428     this.bindings = [];
14429     if(config){
14430         this.addBinding(config);
14431     }
14432     this.enable();
14433 };
14434
14435 Roo.KeyMap.prototype = {
14436     /**
14437      * True to stop the event from bubbling and prevent the default browser action if the
14438      * key was handled by the KeyMap (defaults to false)
14439      * @type Boolean
14440      */
14441     stopEvent : false,
14442
14443     /**
14444      * Add a new binding to this KeyMap. The following config object properties are supported:
14445      * <pre>
14446 Property    Type             Description
14447 ----------  ---------------  ----------------------------------------------------------------------
14448 key         String/Array     A single keycode or an array of keycodes to handle
14449 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14450 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14451 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14452 fn          Function         The function to call when KeyMap finds the expected key combination
14453 scope       Object           The scope of the callback function
14454 </pre>
14455      *
14456      * Usage:
14457      * <pre><code>
14458 // Create a KeyMap
14459 var map = new Roo.KeyMap(document, {
14460     key: Roo.EventObject.ENTER,
14461     fn: handleKey,
14462     scope: this
14463 });
14464
14465 //Add a new binding to the existing KeyMap later
14466 map.addBinding({
14467     key: 'abc',
14468     shift: true,
14469     fn: handleKey,
14470     scope: this
14471 });
14472 </code></pre>
14473      * @param {Object/Array} config A single KeyMap config or an array of configs
14474      */
14475         addBinding : function(config){
14476         if(config instanceof Array){
14477             for(var i = 0, len = config.length; i < len; i++){
14478                 this.addBinding(config[i]);
14479             }
14480             return;
14481         }
14482         var keyCode = config.key,
14483             shift = config.shift, 
14484             ctrl = config.ctrl, 
14485             alt = config.alt,
14486             fn = config.fn,
14487             scope = config.scope;
14488         if(typeof keyCode == "string"){
14489             var ks = [];
14490             var keyString = keyCode.toUpperCase();
14491             for(var j = 0, len = keyString.length; j < len; j++){
14492                 ks.push(keyString.charCodeAt(j));
14493             }
14494             keyCode = ks;
14495         }
14496         var keyArray = keyCode instanceof Array;
14497         var handler = function(e){
14498             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14499                 var k = e.getKey();
14500                 if(keyArray){
14501                     for(var i = 0, len = keyCode.length; i < len; i++){
14502                         if(keyCode[i] == k){
14503                           if(this.stopEvent){
14504                               e.stopEvent();
14505                           }
14506                           fn.call(scope || window, k, e);
14507                           return;
14508                         }
14509                     }
14510                 }else{
14511                     if(k == keyCode){
14512                         if(this.stopEvent){
14513                            e.stopEvent();
14514                         }
14515                         fn.call(scope || window, k, e);
14516                     }
14517                 }
14518             }
14519         };
14520         this.bindings.push(handler);  
14521         },
14522
14523     /**
14524      * Shorthand for adding a single key listener
14525      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14526      * following options:
14527      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14528      * @param {Function} fn The function to call
14529      * @param {Object} scope (optional) The scope of the function
14530      */
14531     on : function(key, fn, scope){
14532         var keyCode, shift, ctrl, alt;
14533         if(typeof key == "object" && !(key instanceof Array)){
14534             keyCode = key.key;
14535             shift = key.shift;
14536             ctrl = key.ctrl;
14537             alt = key.alt;
14538         }else{
14539             keyCode = key;
14540         }
14541         this.addBinding({
14542             key: keyCode,
14543             shift: shift,
14544             ctrl: ctrl,
14545             alt: alt,
14546             fn: fn,
14547             scope: scope
14548         })
14549     },
14550
14551     // private
14552     handleKeyDown : function(e){
14553             if(this.enabled){ //just in case
14554             var b = this.bindings;
14555             for(var i = 0, len = b.length; i < len; i++){
14556                 b[i].call(this, e);
14557             }
14558             }
14559         },
14560         
14561         /**
14562          * Returns true if this KeyMap is enabled
14563          * @return {Boolean} 
14564          */
14565         isEnabled : function(){
14566             return this.enabled;  
14567         },
14568         
14569         /**
14570          * Enables this KeyMap
14571          */
14572         enable: function(){
14573                 if(!this.enabled){
14574                     this.el.on(this.eventName, this.handleKeyDown, this);
14575                     this.enabled = true;
14576                 }
14577         },
14578
14579         /**
14580          * Disable this KeyMap
14581          */
14582         disable: function(){
14583                 if(this.enabled){
14584                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14585                     this.enabled = false;
14586                 }
14587         }
14588 };/*
14589  * Based on:
14590  * Ext JS Library 1.1.1
14591  * Copyright(c) 2006-2007, Ext JS, LLC.
14592  *
14593  * Originally Released Under LGPL - original licence link has changed is not relivant.
14594  *
14595  * Fork - LGPL
14596  * <script type="text/javascript">
14597  */
14598
14599  
14600 /**
14601  * @class Roo.util.TextMetrics
14602  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14603  * wide, in pixels, a given block of text will be.
14604  * @singleton
14605  */
14606 Roo.util.TextMetrics = function(){
14607     var shared;
14608     return {
14609         /**
14610          * Measures the size of the specified text
14611          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14612          * that can affect the size of the rendered text
14613          * @param {String} text The text to measure
14614          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14615          * in order to accurately measure the text height
14616          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14617          */
14618         measure : function(el, text, fixedWidth){
14619             if(!shared){
14620                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14621             }
14622             shared.bind(el);
14623             shared.setFixedWidth(fixedWidth || 'auto');
14624             return shared.getSize(text);
14625         },
14626
14627         /**
14628          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14629          * the overhead of multiple calls to initialize the style properties on each measurement.
14630          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14631          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14632          * in order to accurately measure the text height
14633          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14634          */
14635         createInstance : function(el, fixedWidth){
14636             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14637         }
14638     };
14639 }();
14640
14641  
14642
14643 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14644     var ml = new Roo.Element(document.createElement('div'));
14645     document.body.appendChild(ml.dom);
14646     ml.position('absolute');
14647     ml.setLeftTop(-1000, -1000);
14648     ml.hide();
14649
14650     if(fixedWidth){
14651         ml.setWidth(fixedWidth);
14652     }
14653      
14654     var instance = {
14655         /**
14656          * Returns the size of the specified text based on the internal element's style and width properties
14657          * @memberOf Roo.util.TextMetrics.Instance#
14658          * @param {String} text The text to measure
14659          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14660          */
14661         getSize : function(text){
14662             ml.update(text);
14663             var s = ml.getSize();
14664             ml.update('');
14665             return s;
14666         },
14667
14668         /**
14669          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14670          * that can affect the size of the rendered text
14671          * @memberOf Roo.util.TextMetrics.Instance#
14672          * @param {String/HTMLElement} el The element, dom node or id
14673          */
14674         bind : function(el){
14675             ml.setStyle(
14676                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14677             );
14678         },
14679
14680         /**
14681          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14682          * to set a fixed width in order to accurately measure the text height.
14683          * @memberOf Roo.util.TextMetrics.Instance#
14684          * @param {Number} width The width to set on the element
14685          */
14686         setFixedWidth : function(width){
14687             ml.setWidth(width);
14688         },
14689
14690         /**
14691          * Returns the measured width of the specified text
14692          * @memberOf Roo.util.TextMetrics.Instance#
14693          * @param {String} text The text to measure
14694          * @return {Number} width The width in pixels
14695          */
14696         getWidth : function(text){
14697             ml.dom.style.width = 'auto';
14698             return this.getSize(text).width;
14699         },
14700
14701         /**
14702          * Returns the measured height of the specified text.  For multiline text, be sure to call
14703          * {@link #setFixedWidth} if necessary.
14704          * @memberOf Roo.util.TextMetrics.Instance#
14705          * @param {String} text The text to measure
14706          * @return {Number} height The height in pixels
14707          */
14708         getHeight : function(text){
14709             return this.getSize(text).height;
14710         }
14711     };
14712
14713     instance.bind(bindTo);
14714
14715     return instance;
14716 };
14717
14718 // backwards compat
14719 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14720  * Based on:
14721  * Ext JS Library 1.1.1
14722  * Copyright(c) 2006-2007, Ext JS, LLC.
14723  *
14724  * Originally Released Under LGPL - original licence link has changed is not relivant.
14725  *
14726  * Fork - LGPL
14727  * <script type="text/javascript">
14728  */
14729
14730 /**
14731  * @class Roo.state.Provider
14732  * Abstract base class for state provider implementations. This class provides methods
14733  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14734  * Provider interface.
14735  */
14736 Roo.state.Provider = function(){
14737     /**
14738      * @event statechange
14739      * Fires when a state change occurs.
14740      * @param {Provider} this This state provider
14741      * @param {String} key The state key which was changed
14742      * @param {String} value The encoded value for the state
14743      */
14744     this.addEvents({
14745         "statechange": true
14746     });
14747     this.state = {};
14748     Roo.state.Provider.superclass.constructor.call(this);
14749 };
14750 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14751     /**
14752      * Returns the current value for a key
14753      * @param {String} name The key name
14754      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14755      * @return {Mixed} The state data
14756      */
14757     get : function(name, defaultValue){
14758         return typeof this.state[name] == "undefined" ?
14759             defaultValue : this.state[name];
14760     },
14761     
14762     /**
14763      * Clears a value from the state
14764      * @param {String} name The key name
14765      */
14766     clear : function(name){
14767         delete this.state[name];
14768         this.fireEvent("statechange", this, name, null);
14769     },
14770     
14771     /**
14772      * Sets the value for a key
14773      * @param {String} name The key name
14774      * @param {Mixed} value The value to set
14775      */
14776     set : function(name, value){
14777         this.state[name] = value;
14778         this.fireEvent("statechange", this, name, value);
14779     },
14780     
14781     /**
14782      * Decodes a string previously encoded with {@link #encodeValue}.
14783      * @param {String} value The value to decode
14784      * @return {Mixed} The decoded value
14785      */
14786     decodeValue : function(cookie){
14787         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14788         var matches = re.exec(unescape(cookie));
14789         if(!matches || !matches[1]) return; // non state cookie
14790         var type = matches[1];
14791         var v = matches[2];
14792         switch(type){
14793             case "n":
14794                 return parseFloat(v);
14795             case "d":
14796                 return new Date(Date.parse(v));
14797             case "b":
14798                 return (v == "1");
14799             case "a":
14800                 var all = [];
14801                 var values = v.split("^");
14802                 for(var i = 0, len = values.length; i < len; i++){
14803                     all.push(this.decodeValue(values[i]));
14804                 }
14805                 return all;
14806            case "o":
14807                 var all = {};
14808                 var values = v.split("^");
14809                 for(var i = 0, len = values.length; i < len; i++){
14810                     var kv = values[i].split("=");
14811                     all[kv[0]] = this.decodeValue(kv[1]);
14812                 }
14813                 return all;
14814            default:
14815                 return v;
14816         }
14817     },
14818     
14819     /**
14820      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14821      * @param {Mixed} value The value to encode
14822      * @return {String} The encoded value
14823      */
14824     encodeValue : function(v){
14825         var enc;
14826         if(typeof v == "number"){
14827             enc = "n:" + v;
14828         }else if(typeof v == "boolean"){
14829             enc = "b:" + (v ? "1" : "0");
14830         }else if(v instanceof Date){
14831             enc = "d:" + v.toGMTString();
14832         }else if(v instanceof Array){
14833             var flat = "";
14834             for(var i = 0, len = v.length; i < len; i++){
14835                 flat += this.encodeValue(v[i]);
14836                 if(i != len-1) flat += "^";
14837             }
14838             enc = "a:" + flat;
14839         }else if(typeof v == "object"){
14840             var flat = "";
14841             for(var key in v){
14842                 if(typeof v[key] != "function"){
14843                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14844                 }
14845             }
14846             enc = "o:" + flat.substring(0, flat.length-1);
14847         }else{
14848             enc = "s:" + v;
14849         }
14850         return escape(enc);        
14851     }
14852 });
14853
14854 /*
14855  * Based on:
14856  * Ext JS Library 1.1.1
14857  * Copyright(c) 2006-2007, Ext JS, LLC.
14858  *
14859  * Originally Released Under LGPL - original licence link has changed is not relivant.
14860  *
14861  * Fork - LGPL
14862  * <script type="text/javascript">
14863  */
14864 /**
14865  * @class Roo.state.Manager
14866  * This is the global state manager. By default all components that are "state aware" check this class
14867  * for state information if you don't pass them a custom state provider. In order for this class
14868  * to be useful, it must be initialized with a provider when your application initializes.
14869  <pre><code>
14870 // in your initialization function
14871 init : function(){
14872    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14873    ...
14874    // supposed you have a {@link Roo.BorderLayout}
14875    var layout = new Roo.BorderLayout(...);
14876    layout.restoreState();
14877    // or a {Roo.BasicDialog}
14878    var dialog = new Roo.BasicDialog(...);
14879    dialog.restoreState();
14880  </code></pre>
14881  * @singleton
14882  */
14883 Roo.state.Manager = function(){
14884     var provider = new Roo.state.Provider();
14885     
14886     return {
14887         /**
14888          * Configures the default state provider for your application
14889          * @param {Provider} stateProvider The state provider to set
14890          */
14891         setProvider : function(stateProvider){
14892             provider = stateProvider;
14893         },
14894         
14895         /**
14896          * Returns the current value for a key
14897          * @param {String} name The key name
14898          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14899          * @return {Mixed} The state data
14900          */
14901         get : function(key, defaultValue){
14902             return provider.get(key, defaultValue);
14903         },
14904         
14905         /**
14906          * Sets the value for a key
14907          * @param {String} name The key name
14908          * @param {Mixed} value The state data
14909          */
14910          set : function(key, value){
14911             provider.set(key, value);
14912         },
14913         
14914         /**
14915          * Clears a value from the state
14916          * @param {String} name The key name
14917          */
14918         clear : function(key){
14919             provider.clear(key);
14920         },
14921         
14922         /**
14923          * Gets the currently configured state provider
14924          * @return {Provider} The state provider
14925          */
14926         getProvider : function(){
14927             return provider;
14928         }
14929     };
14930 }();
14931 /*
14932  * Based on:
14933  * Ext JS Library 1.1.1
14934  * Copyright(c) 2006-2007, Ext JS, LLC.
14935  *
14936  * Originally Released Under LGPL - original licence link has changed is not relivant.
14937  *
14938  * Fork - LGPL
14939  * <script type="text/javascript">
14940  */
14941 /**
14942  * @class Roo.state.CookieProvider
14943  * @extends Roo.state.Provider
14944  * The default Provider implementation which saves state via cookies.
14945  * <br />Usage:
14946  <pre><code>
14947    var cp = new Roo.state.CookieProvider({
14948        path: "/cgi-bin/",
14949        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14950        domain: "roojs.com"
14951    })
14952    Roo.state.Manager.setProvider(cp);
14953  </code></pre>
14954  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14955  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14956  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14957  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14958  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14959  * domain the page is running on including the 'www' like 'www.roojs.com')
14960  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14961  * @constructor
14962  * Create a new CookieProvider
14963  * @param {Object} config The configuration object
14964  */
14965 Roo.state.CookieProvider = function(config){
14966     Roo.state.CookieProvider.superclass.constructor.call(this);
14967     this.path = "/";
14968     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14969     this.domain = null;
14970     this.secure = false;
14971     Roo.apply(this, config);
14972     this.state = this.readCookies();
14973 };
14974
14975 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14976     // private
14977     set : function(name, value){
14978         if(typeof value == "undefined" || value === null){
14979             this.clear(name);
14980             return;
14981         }
14982         this.setCookie(name, value);
14983         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14984     },
14985
14986     // private
14987     clear : function(name){
14988         this.clearCookie(name);
14989         Roo.state.CookieProvider.superclass.clear.call(this, name);
14990     },
14991
14992     // private
14993     readCookies : function(){
14994         var cookies = {};
14995         var c = document.cookie + ";";
14996         var re = /\s?(.*?)=(.*?);/g;
14997         var matches;
14998         while((matches = re.exec(c)) != null){
14999             var name = matches[1];
15000             var value = matches[2];
15001             if(name && name.substring(0,3) == "ys-"){
15002                 cookies[name.substr(3)] = this.decodeValue(value);
15003             }
15004         }
15005         return cookies;
15006     },
15007
15008     // private
15009     setCookie : function(name, value){
15010         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15011            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15012            ((this.path == null) ? "" : ("; path=" + this.path)) +
15013            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15014            ((this.secure == true) ? "; secure" : "");
15015     },
15016
15017     // private
15018     clearCookie : function(name){
15019         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15020            ((this.path == null) ? "" : ("; path=" + this.path)) +
15021            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15022            ((this.secure == true) ? "; secure" : "");
15023     }
15024 });/*
15025  * Based on:
15026  * Ext JS Library 1.1.1
15027  * Copyright(c) 2006-2007, Ext JS, LLC.
15028  *
15029  * Originally Released Under LGPL - original licence link has changed is not relivant.
15030  *
15031  * Fork - LGPL
15032  * <script type="text/javascript">
15033  */
15034  
15035
15036 /**
15037  * @class Roo.ComponentMgr
15038  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15039  * @singleton
15040  */
15041 Roo.ComponentMgr = function(){
15042     var all = new Roo.util.MixedCollection();
15043
15044     return {
15045         /**
15046          * Registers a component.
15047          * @param {Roo.Component} c The component
15048          */
15049         register : function(c){
15050             all.add(c);
15051         },
15052
15053         /**
15054          * Unregisters a component.
15055          * @param {Roo.Component} c The component
15056          */
15057         unregister : function(c){
15058             all.remove(c);
15059         },
15060
15061         /**
15062          * Returns a component by id
15063          * @param {String} id The component id
15064          */
15065         get : function(id){
15066             return all.get(id);
15067         },
15068
15069         /**
15070          * Registers a function that will be called when a specified component is added to ComponentMgr
15071          * @param {String} id The component id
15072          * @param {Funtction} fn The callback function
15073          * @param {Object} scope The scope of the callback
15074          */
15075         onAvailable : function(id, fn, scope){
15076             all.on("add", function(index, o){
15077                 if(o.id == id){
15078                     fn.call(scope || o, o);
15079                     all.un("add", fn, scope);
15080                 }
15081             });
15082         }
15083     };
15084 }();/*
15085  * Based on:
15086  * Ext JS Library 1.1.1
15087  * Copyright(c) 2006-2007, Ext JS, LLC.
15088  *
15089  * Originally Released Under LGPL - original licence link has changed is not relivant.
15090  *
15091  * Fork - LGPL
15092  * <script type="text/javascript">
15093  */
15094  
15095 /**
15096  * @class Roo.Component
15097  * @extends Roo.util.Observable
15098  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15099  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15100  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15101  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15102  * All visual components (widgets) that require rendering into a layout should subclass Component.
15103  * @constructor
15104  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15105  * 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
15106  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15107  */
15108 Roo.Component = function(config){
15109     config = config || {};
15110     if(config.tagName || config.dom || typeof config == "string"){ // element object
15111         config = {el: config, id: config.id || config};
15112     }
15113     this.initialConfig = config;
15114
15115     Roo.apply(this, config);
15116     this.addEvents({
15117         /**
15118          * @event disable
15119          * Fires after the component is disabled.
15120              * @param {Roo.Component} this
15121              */
15122         disable : true,
15123         /**
15124          * @event enable
15125          * Fires after the component is enabled.
15126              * @param {Roo.Component} this
15127              */
15128         enable : true,
15129         /**
15130          * @event beforeshow
15131          * Fires before the component is shown.  Return false to stop the show.
15132              * @param {Roo.Component} this
15133              */
15134         beforeshow : true,
15135         /**
15136          * @event show
15137          * Fires after the component is shown.
15138              * @param {Roo.Component} this
15139              */
15140         show : true,
15141         /**
15142          * @event beforehide
15143          * Fires before the component is hidden. Return false to stop the hide.
15144              * @param {Roo.Component} this
15145              */
15146         beforehide : true,
15147         /**
15148          * @event hide
15149          * Fires after the component is hidden.
15150              * @param {Roo.Component} this
15151              */
15152         hide : true,
15153         /**
15154          * @event beforerender
15155          * Fires before the component is rendered. Return false to stop the render.
15156              * @param {Roo.Component} this
15157              */
15158         beforerender : true,
15159         /**
15160          * @event render
15161          * Fires after the component is rendered.
15162              * @param {Roo.Component} this
15163              */
15164         render : true,
15165         /**
15166          * @event beforedestroy
15167          * Fires before the component is destroyed. Return false to stop the destroy.
15168              * @param {Roo.Component} this
15169              */
15170         beforedestroy : true,
15171         /**
15172          * @event destroy
15173          * Fires after the component is destroyed.
15174              * @param {Roo.Component} this
15175              */
15176         destroy : true
15177     });
15178     if(!this.id){
15179         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15180     }
15181     Roo.ComponentMgr.register(this);
15182     Roo.Component.superclass.constructor.call(this);
15183     this.initComponent();
15184     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15185         this.render(this.renderTo);
15186         delete this.renderTo;
15187     }
15188 };
15189
15190 /** @private */
15191 Roo.Component.AUTO_ID = 1000;
15192
15193 Roo.extend(Roo.Component, Roo.util.Observable, {
15194     /**
15195      * @scope Roo.Component.prototype
15196      * @type {Boolean}
15197      * true if this component is hidden. Read-only.
15198      */
15199     hidden : false,
15200     /**
15201      * @type {Boolean}
15202      * true if this component is disabled. Read-only.
15203      */
15204     disabled : false,
15205     /**
15206      * @type {Boolean}
15207      * true if this component has been rendered. Read-only.
15208      */
15209     rendered : false,
15210     
15211     /** @cfg {String} disableClass
15212      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15213      */
15214     disabledClass : "x-item-disabled",
15215         /** @cfg {Boolean} allowDomMove
15216          * Whether the component can move the Dom node when rendering (defaults to true).
15217          */
15218     allowDomMove : true,
15219     /** @cfg {String} hideMode (display|visibility)
15220      * How this component should hidden. Supported values are
15221      * "visibility" (css visibility), "offsets" (negative offset position) and
15222      * "display" (css display) - defaults to "display".
15223      */
15224     hideMode: 'display',
15225
15226     /** @private */
15227     ctype : "Roo.Component",
15228
15229     /**
15230      * @cfg {String} actionMode 
15231      * which property holds the element that used for  hide() / show() / disable() / enable()
15232      * default is 'el' 
15233      */
15234     actionMode : "el",
15235
15236     /** @private */
15237     getActionEl : function(){
15238         return this[this.actionMode];
15239     },
15240
15241     initComponent : Roo.emptyFn,
15242     /**
15243      * If this is a lazy rendering component, render it to its container element.
15244      * @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.
15245      */
15246     render : function(container, position){
15247         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15248             if(!container && this.el){
15249                 this.el = Roo.get(this.el);
15250                 container = this.el.dom.parentNode;
15251                 this.allowDomMove = false;
15252             }
15253             this.container = Roo.get(container);
15254             this.rendered = true;
15255             if(position !== undefined){
15256                 if(typeof position == 'number'){
15257                     position = this.container.dom.childNodes[position];
15258                 }else{
15259                     position = Roo.getDom(position);
15260                 }
15261             }
15262             this.onRender(this.container, position || null);
15263             if(this.cls){
15264                 this.el.addClass(this.cls);
15265                 delete this.cls;
15266             }
15267             if(this.style){
15268                 this.el.applyStyles(this.style);
15269                 delete this.style;
15270             }
15271             this.fireEvent("render", this);
15272             this.afterRender(this.container);
15273             if(this.hidden){
15274                 this.hide();
15275             }
15276             if(this.disabled){
15277                 this.disable();
15278             }
15279         }
15280         return this;
15281     },
15282
15283     /** @private */
15284     // default function is not really useful
15285     onRender : function(ct, position){
15286         if(this.el){
15287             this.el = Roo.get(this.el);
15288             if(this.allowDomMove !== false){
15289                 ct.dom.insertBefore(this.el.dom, position);
15290             }
15291         }
15292     },
15293
15294     /** @private */
15295     getAutoCreate : function(){
15296         var cfg = typeof this.autoCreate == "object" ?
15297                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15298         if(this.id && !cfg.id){
15299             cfg.id = this.id;
15300         }
15301         return cfg;
15302     },
15303
15304     /** @private */
15305     afterRender : Roo.emptyFn,
15306
15307     /**
15308      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15309      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15310      */
15311     destroy : function(){
15312         if(this.fireEvent("beforedestroy", this) !== false){
15313             this.purgeListeners();
15314             this.beforeDestroy();
15315             if(this.rendered){
15316                 this.el.removeAllListeners();
15317                 this.el.remove();
15318                 if(this.actionMode == "container"){
15319                     this.container.remove();
15320                 }
15321             }
15322             this.onDestroy();
15323             Roo.ComponentMgr.unregister(this);
15324             this.fireEvent("destroy", this);
15325         }
15326     },
15327
15328         /** @private */
15329     beforeDestroy : function(){
15330
15331     },
15332
15333         /** @private */
15334         onDestroy : function(){
15335
15336     },
15337
15338     /**
15339      * Returns the underlying {@link Roo.Element}.
15340      * @return {Roo.Element} The element
15341      */
15342     getEl : function(){
15343         return this.el;
15344     },
15345
15346     /**
15347      * Returns the id of this component.
15348      * @return {String}
15349      */
15350     getId : function(){
15351         return this.id;
15352     },
15353
15354     /**
15355      * Try to focus this component.
15356      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15357      * @return {Roo.Component} this
15358      */
15359     focus : function(selectText){
15360         if(this.rendered){
15361             this.el.focus();
15362             if(selectText === true){
15363                 this.el.dom.select();
15364             }
15365         }
15366         return this;
15367     },
15368
15369     /** @private */
15370     blur : function(){
15371         if(this.rendered){
15372             this.el.blur();
15373         }
15374         return this;
15375     },
15376
15377     /**
15378      * Disable this component.
15379      * @return {Roo.Component} this
15380      */
15381     disable : function(){
15382         if(this.rendered){
15383             this.onDisable();
15384         }
15385         this.disabled = true;
15386         this.fireEvent("disable", this);
15387         return this;
15388     },
15389
15390         // private
15391     onDisable : function(){
15392         this.getActionEl().addClass(this.disabledClass);
15393         this.el.dom.disabled = true;
15394     },
15395
15396     /**
15397      * Enable this component.
15398      * @return {Roo.Component} this
15399      */
15400     enable : function(){
15401         if(this.rendered){
15402             this.onEnable();
15403         }
15404         this.disabled = false;
15405         this.fireEvent("enable", this);
15406         return this;
15407     },
15408
15409         // private
15410     onEnable : function(){
15411         this.getActionEl().removeClass(this.disabledClass);
15412         this.el.dom.disabled = false;
15413     },
15414
15415     /**
15416      * Convenience function for setting disabled/enabled by boolean.
15417      * @param {Boolean} disabled
15418      */
15419     setDisabled : function(disabled){
15420         this[disabled ? "disable" : "enable"]();
15421     },
15422
15423     /**
15424      * Show this component.
15425      * @return {Roo.Component} this
15426      */
15427     show: function(){
15428         if(this.fireEvent("beforeshow", this) !== false){
15429             this.hidden = false;
15430             if(this.rendered){
15431                 this.onShow();
15432             }
15433             this.fireEvent("show", this);
15434         }
15435         return this;
15436     },
15437
15438     // private
15439     onShow : function(){
15440         var ae = this.getActionEl();
15441         if(this.hideMode == 'visibility'){
15442             ae.dom.style.visibility = "visible";
15443         }else if(this.hideMode == 'offsets'){
15444             ae.removeClass('x-hidden');
15445         }else{
15446             ae.dom.style.display = "";
15447         }
15448     },
15449
15450     /**
15451      * Hide this component.
15452      * @return {Roo.Component} this
15453      */
15454     hide: function(){
15455         if(this.fireEvent("beforehide", this) !== false){
15456             this.hidden = true;
15457             if(this.rendered){
15458                 this.onHide();
15459             }
15460             this.fireEvent("hide", this);
15461         }
15462         return this;
15463     },
15464
15465     // private
15466     onHide : function(){
15467         var ae = this.getActionEl();
15468         if(this.hideMode == 'visibility'){
15469             ae.dom.style.visibility = "hidden";
15470         }else if(this.hideMode == 'offsets'){
15471             ae.addClass('x-hidden');
15472         }else{
15473             ae.dom.style.display = "none";
15474         }
15475     },
15476
15477     /**
15478      * Convenience function to hide or show this component by boolean.
15479      * @param {Boolean} visible True to show, false to hide
15480      * @return {Roo.Component} this
15481      */
15482     setVisible: function(visible){
15483         if(visible) {
15484             this.show();
15485         }else{
15486             this.hide();
15487         }
15488         return this;
15489     },
15490
15491     /**
15492      * Returns true if this component is visible.
15493      */
15494     isVisible : function(){
15495         return this.getActionEl().isVisible();
15496     },
15497
15498     cloneConfig : function(overrides){
15499         overrides = overrides || {};
15500         var id = overrides.id || Roo.id();
15501         var cfg = Roo.applyIf(overrides, this.initialConfig);
15502         cfg.id = id; // prevent dup id
15503         return new this.constructor(cfg);
15504     }
15505 });/*
15506  * Based on:
15507  * Ext JS Library 1.1.1
15508  * Copyright(c) 2006-2007, Ext JS, LLC.
15509  *
15510  * Originally Released Under LGPL - original licence link has changed is not relivant.
15511  *
15512  * Fork - LGPL
15513  * <script type="text/javascript">
15514  */
15515
15516 /**
15517  * @class Roo.BoxComponent
15518  * @extends Roo.Component
15519  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15520  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15521  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15522  * layout containers.
15523  * @constructor
15524  * @param {Roo.Element/String/Object} config The configuration options.
15525  */
15526 Roo.BoxComponent = function(config){
15527     Roo.Component.call(this, config);
15528     this.addEvents({
15529         /**
15530          * @event resize
15531          * Fires after the component is resized.
15532              * @param {Roo.Component} this
15533              * @param {Number} adjWidth The box-adjusted width that was set
15534              * @param {Number} adjHeight The box-adjusted height that was set
15535              * @param {Number} rawWidth The width that was originally specified
15536              * @param {Number} rawHeight The height that was originally specified
15537              */
15538         resize : true,
15539         /**
15540          * @event move
15541          * Fires after the component is moved.
15542              * @param {Roo.Component} this
15543              * @param {Number} x The new x position
15544              * @param {Number} y The new y position
15545              */
15546         move : true
15547     });
15548 };
15549
15550 Roo.extend(Roo.BoxComponent, Roo.Component, {
15551     // private, set in afterRender to signify that the component has been rendered
15552     boxReady : false,
15553     // private, used to defer height settings to subclasses
15554     deferHeight: false,
15555     /** @cfg {Number} width
15556      * width (optional) size of component
15557      */
15558      /** @cfg {Number} height
15559      * height (optional) size of component
15560      */
15561      
15562     /**
15563      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15564      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15565      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15566      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15567      * @return {Roo.BoxComponent} this
15568      */
15569     setSize : function(w, h){
15570         // support for standard size objects
15571         if(typeof w == 'object'){
15572             h = w.height;
15573             w = w.width;
15574         }
15575         // not rendered
15576         if(!this.boxReady){
15577             this.width = w;
15578             this.height = h;
15579             return this;
15580         }
15581
15582         // prevent recalcs when not needed
15583         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15584             return this;
15585         }
15586         this.lastSize = {width: w, height: h};
15587
15588         var adj = this.adjustSize(w, h);
15589         var aw = adj.width, ah = adj.height;
15590         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15591             var rz = this.getResizeEl();
15592             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15593                 rz.setSize(aw, ah);
15594             }else if(!this.deferHeight && ah !== undefined){
15595                 rz.setHeight(ah);
15596             }else if(aw !== undefined){
15597                 rz.setWidth(aw);
15598             }
15599             this.onResize(aw, ah, w, h);
15600             this.fireEvent('resize', this, aw, ah, w, h);
15601         }
15602         return this;
15603     },
15604
15605     /**
15606      * Gets the current size of the component's underlying element.
15607      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15608      */
15609     getSize : function(){
15610         return this.el.getSize();
15611     },
15612
15613     /**
15614      * Gets the current XY position of the component's underlying element.
15615      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15616      * @return {Array} The XY position of the element (e.g., [100, 200])
15617      */
15618     getPosition : function(local){
15619         if(local === true){
15620             return [this.el.getLeft(true), this.el.getTop(true)];
15621         }
15622         return this.xy || this.el.getXY();
15623     },
15624
15625     /**
15626      * Gets the current box measurements of the component's underlying element.
15627      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15628      * @returns {Object} box An object in the format {x, y, width, height}
15629      */
15630     getBox : function(local){
15631         var s = this.el.getSize();
15632         if(local){
15633             s.x = this.el.getLeft(true);
15634             s.y = this.el.getTop(true);
15635         }else{
15636             var xy = this.xy || this.el.getXY();
15637             s.x = xy[0];
15638             s.y = xy[1];
15639         }
15640         return s;
15641     },
15642
15643     /**
15644      * Sets the current box measurements of the component's underlying element.
15645      * @param {Object} box An object in the format {x, y, width, height}
15646      * @returns {Roo.BoxComponent} this
15647      */
15648     updateBox : function(box){
15649         this.setSize(box.width, box.height);
15650         this.setPagePosition(box.x, box.y);
15651         return this;
15652     },
15653
15654     // protected
15655     getResizeEl : function(){
15656         return this.resizeEl || this.el;
15657     },
15658
15659     // protected
15660     getPositionEl : function(){
15661         return this.positionEl || this.el;
15662     },
15663
15664     /**
15665      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15666      * This method fires the move event.
15667      * @param {Number} left The new left
15668      * @param {Number} top The new top
15669      * @returns {Roo.BoxComponent} this
15670      */
15671     setPosition : function(x, y){
15672         this.x = x;
15673         this.y = y;
15674         if(!this.boxReady){
15675             return this;
15676         }
15677         var adj = this.adjustPosition(x, y);
15678         var ax = adj.x, ay = adj.y;
15679
15680         var el = this.getPositionEl();
15681         if(ax !== undefined || ay !== undefined){
15682             if(ax !== undefined && ay !== undefined){
15683                 el.setLeftTop(ax, ay);
15684             }else if(ax !== undefined){
15685                 el.setLeft(ax);
15686             }else if(ay !== undefined){
15687                 el.setTop(ay);
15688             }
15689             this.onPosition(ax, ay);
15690             this.fireEvent('move', this, ax, ay);
15691         }
15692         return this;
15693     },
15694
15695     /**
15696      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15697      * This method fires the move event.
15698      * @param {Number} x The new x position
15699      * @param {Number} y The new y position
15700      * @returns {Roo.BoxComponent} this
15701      */
15702     setPagePosition : function(x, y){
15703         this.pageX = x;
15704         this.pageY = y;
15705         if(!this.boxReady){
15706             return;
15707         }
15708         if(x === undefined || y === undefined){ // cannot translate undefined points
15709             return;
15710         }
15711         var p = this.el.translatePoints(x, y);
15712         this.setPosition(p.left, p.top);
15713         return this;
15714     },
15715
15716     // private
15717     onRender : function(ct, position){
15718         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15719         if(this.resizeEl){
15720             this.resizeEl = Roo.get(this.resizeEl);
15721         }
15722         if(this.positionEl){
15723             this.positionEl = Roo.get(this.positionEl);
15724         }
15725     },
15726
15727     // private
15728     afterRender : function(){
15729         Roo.BoxComponent.superclass.afterRender.call(this);
15730         this.boxReady = true;
15731         this.setSize(this.width, this.height);
15732         if(this.x || this.y){
15733             this.setPosition(this.x, this.y);
15734         }
15735         if(this.pageX || this.pageY){
15736             this.setPagePosition(this.pageX, this.pageY);
15737         }
15738     },
15739
15740     /**
15741      * Force the component's size to recalculate based on the underlying element's current height and width.
15742      * @returns {Roo.BoxComponent} this
15743      */
15744     syncSize : function(){
15745         delete this.lastSize;
15746         this.setSize(this.el.getWidth(), this.el.getHeight());
15747         return this;
15748     },
15749
15750     /**
15751      * Called after the component is resized, this method is empty by default but can be implemented by any
15752      * subclass that needs to perform custom logic after a resize occurs.
15753      * @param {Number} adjWidth The box-adjusted width that was set
15754      * @param {Number} adjHeight The box-adjusted height that was set
15755      * @param {Number} rawWidth The width that was originally specified
15756      * @param {Number} rawHeight The height that was originally specified
15757      */
15758     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15759
15760     },
15761
15762     /**
15763      * Called after the component is moved, this method is empty by default but can be implemented by any
15764      * subclass that needs to perform custom logic after a move occurs.
15765      * @param {Number} x The new x position
15766      * @param {Number} y The new y position
15767      */
15768     onPosition : function(x, y){
15769
15770     },
15771
15772     // private
15773     adjustSize : function(w, h){
15774         if(this.autoWidth){
15775             w = 'auto';
15776         }
15777         if(this.autoHeight){
15778             h = 'auto';
15779         }
15780         return {width : w, height: h};
15781     },
15782
15783     // private
15784     adjustPosition : function(x, y){
15785         return {x : x, y: y};
15786     }
15787 });/*
15788  * Original code for Roojs - LGPL
15789  * <script type="text/javascript">
15790  */
15791  
15792 /**
15793  * @class Roo.XComponent
15794  * A delayed Element creator...
15795  * Or a way to group chunks of interface together.
15796  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15797  *  used in conjunction with XComponent.build() it will create an instance of each element,
15798  *  then call addxtype() to build the User interface.
15799  * 
15800  * Mypart.xyx = new Roo.XComponent({
15801
15802     parent : 'Mypart.xyz', // empty == document.element.!!
15803     order : '001',
15804     name : 'xxxx'
15805     region : 'xxxx'
15806     disabled : function() {} 
15807      
15808     tree : function() { // return an tree of xtype declared components
15809         var MODULE = this;
15810         return 
15811         {
15812             xtype : 'NestedLayoutPanel',
15813             // technicall
15814         }
15815      ]
15816  *})
15817  *
15818  *
15819  * It can be used to build a big heiracy, with parent etc.
15820  * or you can just use this to render a single compoent to a dom element
15821  * MYPART.render(Roo.Element | String(id) | dom_element )
15822  *
15823  *
15824  * Usage patterns.
15825  *
15826  * Classic Roo
15827  *
15828  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15829  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15830  *
15831  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15832  *
15833  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15834  * - if mulitple topModules exist, the last one is defined as the top module.
15835  *
15836  * Embeded Roo
15837  * 
15838  * When the top level or multiple modules are to embedded into a existing HTML page,
15839  * the parent element can container '#id' of the element where the module will be drawn.
15840  *
15841  * Bootstrap Roo
15842  *
15843  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15844  * it relies more on a include mechanism, where sub modules are included into an outer page.
15845  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15846  * 
15847  * Bootstrap Roo Included elements
15848  *
15849  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15850  * hence confusing the component builder as it thinks there are multiple top level elements. 
15851  *
15852  * 
15853  * 
15854  * @extends Roo.util.Observable
15855  * @constructor
15856  * @param cfg {Object} configuration of component
15857  * 
15858  */
15859 Roo.XComponent = function(cfg) {
15860     Roo.apply(this, cfg);
15861     this.addEvents({ 
15862         /**
15863              * @event built
15864              * Fires when this the componnt is built
15865              * @param {Roo.XComponent} c the component
15866              */
15867         'built' : true
15868         
15869     });
15870     this.region = this.region || 'center'; // default..
15871     Roo.XComponent.register(this);
15872     this.modules = false;
15873     this.el = false; // where the layout goes..
15874     
15875     
15876 }
15877 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15878     /**
15879      * @property el
15880      * The created element (with Roo.factory())
15881      * @type {Roo.Layout}
15882      */
15883     el  : false,
15884     
15885     /**
15886      * @property el
15887      * for BC  - use el in new code
15888      * @type {Roo.Layout}
15889      */
15890     panel : false,
15891     
15892     /**
15893      * @property layout
15894      * for BC  - use el in new code
15895      * @type {Roo.Layout}
15896      */
15897     layout : false,
15898     
15899      /**
15900      * @cfg {Function|boolean} disabled
15901      * If this module is disabled by some rule, return true from the funtion
15902      */
15903     disabled : false,
15904     
15905     /**
15906      * @cfg {String} parent 
15907      * Name of parent element which it get xtype added to..
15908      */
15909     parent: false,
15910     
15911     /**
15912      * @cfg {String} order
15913      * Used to set the order in which elements are created (usefull for multiple tabs)
15914      */
15915     
15916     order : false,
15917     /**
15918      * @cfg {String} name
15919      * String to display while loading.
15920      */
15921     name : false,
15922     /**
15923      * @cfg {String} region
15924      * Region to render component to (defaults to center)
15925      */
15926     region : 'center',
15927     
15928     /**
15929      * @cfg {Array} items
15930      * A single item array - the first element is the root of the tree..
15931      * It's done this way to stay compatible with the Xtype system...
15932      */
15933     items : false,
15934     
15935     /**
15936      * @property _tree
15937      * The method that retuns the tree of parts that make up this compoennt 
15938      * @type {function}
15939      */
15940     _tree  : false,
15941     
15942      /**
15943      * render
15944      * render element to dom or tree
15945      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15946      */
15947     
15948     render : function(el)
15949     {
15950         
15951         el = el || false;
15952         var hp = this.parent ? 1 : 0;
15953         Roo.debug &&  Roo.log(this);
15954         
15955         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15956             // if parent is a '#.....' string, then let's use that..
15957             var ename = this.parent.substr(1);
15958             this.parent = false;
15959             Roo.debug && Roo.log(ename);
15960             switch (ename) {
15961                 case 'bootstrap-body' :
15962                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15963                         this.parent = { el :  new  Roo.bootstrap.Body() };
15964                         Roo.debug && Roo.log("setting el to doc body");
15965                          
15966                     } else {
15967                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15968                     }
15969                     break;
15970                 case 'bootstrap':
15971                     this.parent = { el : true};
15972                     // fall through
15973                 default:
15974                     el = Roo.get(ename);
15975                     break;
15976             }
15977                 
15978             
15979             if (!el && !this.parent) {
15980                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15981                 return;
15982             }
15983         }
15984         Roo.debug && Roo.log("EL:");
15985         Roo.debug && Roo.log(el);
15986         Roo.debug && Roo.log("this.parent.el:");
15987         Roo.debug && Roo.log(this.parent.el);
15988         
15989         var tree = this._tree ? this._tree() : this.tree();
15990
15991         // altertive root elements ??? - we need a better way to indicate these.
15992         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15993                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15994         
15995         if (!this.parent && is_alt) {
15996             //el = Roo.get(document.body);
15997             this.parent = { el : true };
15998         }
15999             
16000             
16001         
16002         if (!this.parent) {
16003             
16004             Roo.debug && Roo.log("no parent - creating one");
16005             
16006             el = el ? Roo.get(el) : false;      
16007             
16008             // it's a top level one..
16009             this.parent =  {
16010                 el : new Roo.BorderLayout(el || document.body, {
16011                 
16012                      center: {
16013                          titlebar: false,
16014                          autoScroll:false,
16015                          closeOnTab: true,
16016                          tabPosition: 'top',
16017                           //resizeTabs: true,
16018                          alwaysShowTabs: el && hp? false :  true,
16019                          hideTabs: el || !hp ? true :  false,
16020                          minTabWidth: 140
16021                      }
16022                  })
16023             }
16024         }
16025         
16026         if (!this.parent.el) {
16027                 // probably an old style ctor, which has been disabled.
16028                 return;
16029
16030         }
16031                 // The 'tree' method is  '_tree now' 
16032             
16033         tree.region = tree.region || this.region;
16034         
16035         if (this.parent.el === true) {
16036             // bootstrap... - body..
16037             this.parent.el = Roo.factory(tree);
16038         }
16039         
16040         this.el = this.parent.el.addxtype(tree);
16041         this.fireEvent('built', this);
16042         
16043         this.panel = this.el;
16044         this.layout = this.panel.layout;
16045         this.parentLayout = this.parent.layout  || false;  
16046          
16047     }
16048     
16049 });
16050
16051 Roo.apply(Roo.XComponent, {
16052     /**
16053      * @property  hideProgress
16054      * true to disable the building progress bar.. usefull on single page renders.
16055      * @type Boolean
16056      */
16057     hideProgress : false,
16058     /**
16059      * @property  buildCompleted
16060      * True when the builder has completed building the interface.
16061      * @type Boolean
16062      */
16063     buildCompleted : false,
16064      
16065     /**
16066      * @property  topModule
16067      * the upper most module - uses document.element as it's constructor.
16068      * @type Object
16069      */
16070      
16071     topModule  : false,
16072       
16073     /**
16074      * @property  modules
16075      * array of modules to be created by registration system.
16076      * @type {Array} of Roo.XComponent
16077      */
16078     
16079     modules : [],
16080     /**
16081      * @property  elmodules
16082      * array of modules to be created by which use #ID 
16083      * @type {Array} of Roo.XComponent
16084      */
16085      
16086     elmodules : [],
16087
16088      /**
16089      * @property  build_from_html
16090      * Build elements from html - used by bootstrap HTML stuff 
16091      *    - this is cleared after build is completed
16092      * @type {boolean} true  (default false)
16093      */
16094      
16095     build_from_html : false,
16096
16097     /**
16098      * Register components to be built later.
16099      *
16100      * This solves the following issues
16101      * - Building is not done on page load, but after an authentication process has occured.
16102      * - Interface elements are registered on page load
16103      * - Parent Interface elements may not be loaded before child, so this handles that..
16104      * 
16105      *
16106      * example:
16107      * 
16108      * MyApp.register({
16109           order : '000001',
16110           module : 'Pman.Tab.projectMgr',
16111           region : 'center',
16112           parent : 'Pman.layout',
16113           disabled : false,  // or use a function..
16114         })
16115      
16116      * * @param {Object} details about module
16117      */
16118     register : function(obj) {
16119                 
16120         Roo.XComponent.event.fireEvent('register', obj);
16121         switch(typeof(obj.disabled) ) {
16122                 
16123             case 'undefined':
16124                 break;
16125             
16126             case 'function':
16127                 if ( obj.disabled() ) {
16128                         return;
16129                 }
16130                 break;
16131             
16132             default:
16133                 if (obj.disabled) {
16134                         return;
16135                 }
16136                 break;
16137         }
16138                 
16139         this.modules.push(obj);
16140          
16141     },
16142     /**
16143      * convert a string to an object..
16144      * eg. 'AAA.BBB' -> finds AAA.BBB
16145
16146      */
16147     
16148     toObject : function(str)
16149     {
16150         if (!str || typeof(str) == 'object') {
16151             return str;
16152         }
16153         if (str.substring(0,1) == '#') {
16154             return str;
16155         }
16156
16157         var ar = str.split('.');
16158         var rt, o;
16159         rt = ar.shift();
16160             /** eval:var:o */
16161         try {
16162             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16163         } catch (e) {
16164             throw "Module not found : " + str;
16165         }
16166         
16167         if (o === false) {
16168             throw "Module not found : " + str;
16169         }
16170         Roo.each(ar, function(e) {
16171             if (typeof(o[e]) == 'undefined') {
16172                 throw "Module not found : " + str;
16173             }
16174             o = o[e];
16175         });
16176         
16177         return o;
16178         
16179     },
16180     
16181     
16182     /**
16183      * move modules into their correct place in the tree..
16184      * 
16185      */
16186     preBuild : function ()
16187     {
16188         var _t = this;
16189         Roo.each(this.modules , function (obj)
16190         {
16191             Roo.XComponent.event.fireEvent('beforebuild', obj);
16192             
16193             var opar = obj.parent;
16194             try { 
16195                 obj.parent = this.toObject(opar);
16196             } catch(e) {
16197                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16198                 return;
16199             }
16200             
16201             if (!obj.parent) {
16202                 Roo.debug && Roo.log("GOT top level module");
16203                 Roo.debug && Roo.log(obj);
16204                 obj.modules = new Roo.util.MixedCollection(false, 
16205                     function(o) { return o.order + '' }
16206                 );
16207                 this.topModule = obj;
16208                 return;
16209             }
16210                         // parent is a string (usually a dom element name..)
16211             if (typeof(obj.parent) == 'string') {
16212                 this.elmodules.push(obj);
16213                 return;
16214             }
16215             if (obj.parent.constructor != Roo.XComponent) {
16216                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16217             }
16218             if (!obj.parent.modules) {
16219                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16220                     function(o) { return o.order + '' }
16221                 );
16222             }
16223             if (obj.parent.disabled) {
16224                 obj.disabled = true;
16225             }
16226             obj.parent.modules.add(obj);
16227         }, this);
16228     },
16229     
16230      /**
16231      * make a list of modules to build.
16232      * @return {Array} list of modules. 
16233      */ 
16234     
16235     buildOrder : function()
16236     {
16237         var _this = this;
16238         var cmp = function(a,b) {   
16239             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16240         };
16241         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16242             throw "No top level modules to build";
16243         }
16244         
16245         // make a flat list in order of modules to build.
16246         var mods = this.topModule ? [ this.topModule ] : [];
16247                 
16248         
16249         // elmodules (is a list of DOM based modules )
16250         Roo.each(this.elmodules, function(e) {
16251             mods.push(e);
16252             if (!this.topModule &&
16253                 typeof(e.parent) == 'string' &&
16254                 e.parent.substring(0,1) == '#' &&
16255                 Roo.get(e.parent.substr(1))
16256                ) {
16257                 
16258                 _this.topModule = e;
16259             }
16260             
16261         });
16262
16263         
16264         // add modules to their parents..
16265         var addMod = function(m) {
16266             Roo.debug && Roo.log("build Order: add: " + m.name);
16267                 
16268             mods.push(m);
16269             if (m.modules && !m.disabled) {
16270                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16271                 m.modules.keySort('ASC',  cmp );
16272                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16273     
16274                 m.modules.each(addMod);
16275             } else {
16276                 Roo.debug && Roo.log("build Order: no child modules");
16277             }
16278             // not sure if this is used any more..
16279             if (m.finalize) {
16280                 m.finalize.name = m.name + " (clean up) ";
16281                 mods.push(m.finalize);
16282             }
16283             
16284         }
16285         if (this.topModule && this.topModule.modules) { 
16286             this.topModule.modules.keySort('ASC',  cmp );
16287             this.topModule.modules.each(addMod);
16288         } 
16289         return mods;
16290     },
16291     
16292      /**
16293      * Build the registered modules.
16294      * @param {Object} parent element.
16295      * @param {Function} optional method to call after module has been added.
16296      * 
16297      */ 
16298    
16299     build : function(opts) 
16300     {
16301         
16302         if (typeof(opts) != 'undefined') {
16303             Roo.apply(this,opts);
16304         }
16305         
16306         this.preBuild();
16307         var mods = this.buildOrder();
16308       
16309         //this.allmods = mods;
16310         //Roo.debug && Roo.log(mods);
16311         //return;
16312         if (!mods.length) { // should not happen
16313             throw "NO modules!!!";
16314         }
16315         
16316         
16317         var msg = "Building Interface...";
16318         // flash it up as modal - so we store the mask!?
16319         if (!this.hideProgress && Roo.MessageBox) {
16320             Roo.MessageBox.show({ title: 'loading' });
16321             Roo.MessageBox.show({
16322                title: "Please wait...",
16323                msg: msg,
16324                width:450,
16325                progress:true,
16326                closable:false,
16327                modal: false
16328               
16329             });
16330         }
16331         var total = mods.length;
16332         
16333         var _this = this;
16334         var progressRun = function() {
16335             if (!mods.length) {
16336                 Roo.debug && Roo.log('hide?');
16337                 if (!this.hideProgress && Roo.MessageBox) {
16338                     Roo.MessageBox.hide();
16339                 }
16340                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16341                 
16342                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16343                 
16344                 // THE END...
16345                 return false;   
16346             }
16347             
16348             var m = mods.shift();
16349             
16350             
16351             Roo.debug && Roo.log(m);
16352             // not sure if this is supported any more.. - modules that are are just function
16353             if (typeof(m) == 'function') { 
16354                 m.call(this);
16355                 return progressRun.defer(10, _this);
16356             } 
16357             
16358             
16359             msg = "Building Interface " + (total  - mods.length) + 
16360                     " of " + total + 
16361                     (m.name ? (' - ' + m.name) : '');
16362                         Roo.debug && Roo.log(msg);
16363             if (!this.hideProgress &&  Roo.MessageBox) { 
16364                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16365             }
16366             
16367          
16368             // is the module disabled?
16369             var disabled = (typeof(m.disabled) == 'function') ?
16370                 m.disabled.call(m.module.disabled) : m.disabled;    
16371             
16372             
16373             if (disabled) {
16374                 return progressRun(); // we do not update the display!
16375             }
16376             
16377             // now build 
16378             
16379                         
16380                         
16381             m.render();
16382             // it's 10 on top level, and 1 on others??? why...
16383             return progressRun.defer(10, _this);
16384              
16385         }
16386         progressRun.defer(1, _this);
16387      
16388         
16389         
16390     },
16391         
16392         
16393         /**
16394          * Event Object.
16395          *
16396          *
16397          */
16398         event: false, 
16399     /**
16400          * wrapper for event.on - aliased later..  
16401          * Typically use to register a event handler for register:
16402          *
16403          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16404          *
16405          */
16406     on : false
16407    
16408     
16409     
16410 });
16411
16412 Roo.XComponent.event = new Roo.util.Observable({
16413                 events : { 
16414                         /**
16415                          * @event register
16416                          * Fires when an Component is registered,
16417                          * set the disable property on the Component to stop registration.
16418                          * @param {Roo.XComponent} c the component being registerd.
16419                          * 
16420                          */
16421                         'register' : true,
16422             /**
16423                          * @event beforebuild
16424                          * Fires before each Component is built
16425                          * can be used to apply permissions.
16426                          * @param {Roo.XComponent} c the component being registerd.
16427                          * 
16428                          */
16429                         'beforebuild' : true,
16430                         /**
16431                          * @event buildcomplete
16432                          * Fires on the top level element when all elements have been built
16433                          * @param {Roo.XComponent} the top level component.
16434                          */
16435                         'buildcomplete' : true
16436                         
16437                 }
16438 });
16439
16440 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16441  /*
16442  * Based on:
16443  * Ext JS Library 1.1.1
16444  * Copyright(c) 2006-2007, Ext JS, LLC.
16445  *
16446  * Originally Released Under LGPL - original licence link has changed is not relivant.
16447  *
16448  * Fork - LGPL
16449  * <script type="text/javascript">
16450  */
16451
16452
16453
16454 /*
16455  * These classes are derivatives of the similarly named classes in the YUI Library.
16456  * The original license:
16457  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16458  * Code licensed under the BSD License:
16459  * http://developer.yahoo.net/yui/license.txt
16460  */
16461
16462 (function() {
16463
16464 var Event=Roo.EventManager;
16465 var Dom=Roo.lib.Dom;
16466
16467 /**
16468  * @class Roo.dd.DragDrop
16469  * @extends Roo.util.Observable
16470  * Defines the interface and base operation of items that that can be
16471  * dragged or can be drop targets.  It was designed to be extended, overriding
16472  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16473  * Up to three html elements can be associated with a DragDrop instance:
16474  * <ul>
16475  * <li>linked element: the element that is passed into the constructor.
16476  * This is the element which defines the boundaries for interaction with
16477  * other DragDrop objects.</li>
16478  * <li>handle element(s): The drag operation only occurs if the element that
16479  * was clicked matches a handle element.  By default this is the linked
16480  * element, but there are times that you will want only a portion of the
16481  * linked element to initiate the drag operation, and the setHandleElId()
16482  * method provides a way to define this.</li>
16483  * <li>drag element: this represents the element that would be moved along
16484  * with the cursor during a drag operation.  By default, this is the linked
16485  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16486  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16487  * </li>
16488  * </ul>
16489  * This class should not be instantiated until the onload event to ensure that
16490  * the associated elements are available.
16491  * The following would define a DragDrop obj that would interact with any
16492  * other DragDrop obj in the "group1" group:
16493  * <pre>
16494  *  dd = new Roo.dd.DragDrop("div1", "group1");
16495  * </pre>
16496  * Since none of the event handlers have been implemented, nothing would
16497  * actually happen if you were to run the code above.  Normally you would
16498  * override this class or one of the default implementations, but you can
16499  * also override the methods you want on an instance of the class...
16500  * <pre>
16501  *  dd.onDragDrop = function(e, id) {
16502  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16503  *  }
16504  * </pre>
16505  * @constructor
16506  * @param {String} id of the element that is linked to this instance
16507  * @param {String} sGroup the group of related DragDrop objects
16508  * @param {object} config an object containing configurable attributes
16509  *                Valid properties for DragDrop:
16510  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16511  */
16512 Roo.dd.DragDrop = function(id, sGroup, config) {
16513     if (id) {
16514         this.init(id, sGroup, config);
16515     }
16516     
16517 };
16518
16519 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16520
16521     /**
16522      * The id of the element associated with this object.  This is what we
16523      * refer to as the "linked element" because the size and position of
16524      * this element is used to determine when the drag and drop objects have
16525      * interacted.
16526      * @property id
16527      * @type String
16528      */
16529     id: null,
16530
16531     /**
16532      * Configuration attributes passed into the constructor
16533      * @property config
16534      * @type object
16535      */
16536     config: null,
16537
16538     /**
16539      * The id of the element that will be dragged.  By default this is same
16540      * as the linked element , but could be changed to another element. Ex:
16541      * Roo.dd.DDProxy
16542      * @property dragElId
16543      * @type String
16544      * @private
16545      */
16546     dragElId: null,
16547
16548     /**
16549      * the id of the element that initiates the drag operation.  By default
16550      * this is the linked element, but could be changed to be a child of this
16551      * element.  This lets us do things like only starting the drag when the
16552      * header element within the linked html element is clicked.
16553      * @property handleElId
16554      * @type String
16555      * @private
16556      */
16557     handleElId: null,
16558
16559     /**
16560      * An associative array of HTML tags that will be ignored if clicked.
16561      * @property invalidHandleTypes
16562      * @type {string: string}
16563      */
16564     invalidHandleTypes: null,
16565
16566     /**
16567      * An associative array of ids for elements that will be ignored if clicked
16568      * @property invalidHandleIds
16569      * @type {string: string}
16570      */
16571     invalidHandleIds: null,
16572
16573     /**
16574      * An indexted array of css class names for elements that will be ignored
16575      * if clicked.
16576      * @property invalidHandleClasses
16577      * @type string[]
16578      */
16579     invalidHandleClasses: null,
16580
16581     /**
16582      * The linked element's absolute X position at the time the drag was
16583      * started
16584      * @property startPageX
16585      * @type int
16586      * @private
16587      */
16588     startPageX: 0,
16589
16590     /**
16591      * The linked element's absolute X position at the time the drag was
16592      * started
16593      * @property startPageY
16594      * @type int
16595      * @private
16596      */
16597     startPageY: 0,
16598
16599     /**
16600      * The group defines a logical collection of DragDrop objects that are
16601      * related.  Instances only get events when interacting with other
16602      * DragDrop object in the same group.  This lets us define multiple
16603      * groups using a single DragDrop subclass if we want.
16604      * @property groups
16605      * @type {string: string}
16606      */
16607     groups: null,
16608
16609     /**
16610      * Individual drag/drop instances can be locked.  This will prevent
16611      * onmousedown start drag.
16612      * @property locked
16613      * @type boolean
16614      * @private
16615      */
16616     locked: false,
16617
16618     /**
16619      * Lock this instance
16620      * @method lock
16621      */
16622     lock: function() { this.locked = true; },
16623
16624     /**
16625      * Unlock this instace
16626      * @method unlock
16627      */
16628     unlock: function() { this.locked = false; },
16629
16630     /**
16631      * By default, all insances can be a drop target.  This can be disabled by
16632      * setting isTarget to false.
16633      * @method isTarget
16634      * @type boolean
16635      */
16636     isTarget: true,
16637
16638     /**
16639      * The padding configured for this drag and drop object for calculating
16640      * the drop zone intersection with this object.
16641      * @method padding
16642      * @type int[]
16643      */
16644     padding: null,
16645
16646     /**
16647      * Cached reference to the linked element
16648      * @property _domRef
16649      * @private
16650      */
16651     _domRef: null,
16652
16653     /**
16654      * Internal typeof flag
16655      * @property __ygDragDrop
16656      * @private
16657      */
16658     __ygDragDrop: true,
16659
16660     /**
16661      * Set to true when horizontal contraints are applied
16662      * @property constrainX
16663      * @type boolean
16664      * @private
16665      */
16666     constrainX: false,
16667
16668     /**
16669      * Set to true when vertical contraints are applied
16670      * @property constrainY
16671      * @type boolean
16672      * @private
16673      */
16674     constrainY: false,
16675
16676     /**
16677      * The left constraint
16678      * @property minX
16679      * @type int
16680      * @private
16681      */
16682     minX: 0,
16683
16684     /**
16685      * The right constraint
16686      * @property maxX
16687      * @type int
16688      * @private
16689      */
16690     maxX: 0,
16691
16692     /**
16693      * The up constraint
16694      * @property minY
16695      * @type int
16696      * @type int
16697      * @private
16698      */
16699     minY: 0,
16700
16701     /**
16702      * The down constraint
16703      * @property maxY
16704      * @type int
16705      * @private
16706      */
16707     maxY: 0,
16708
16709     /**
16710      * Maintain offsets when we resetconstraints.  Set to true when you want
16711      * the position of the element relative to its parent to stay the same
16712      * when the page changes
16713      *
16714      * @property maintainOffset
16715      * @type boolean
16716      */
16717     maintainOffset: false,
16718
16719     /**
16720      * Array of pixel locations the element will snap to if we specified a
16721      * horizontal graduation/interval.  This array is generated automatically
16722      * when you define a tick interval.
16723      * @property xTicks
16724      * @type int[]
16725      */
16726     xTicks: null,
16727
16728     /**
16729      * Array of pixel locations the element will snap to if we specified a
16730      * vertical graduation/interval.  This array is generated automatically
16731      * when you define a tick interval.
16732      * @property yTicks
16733      * @type int[]
16734      */
16735     yTicks: null,
16736
16737     /**
16738      * By default the drag and drop instance will only respond to the primary
16739      * button click (left button for a right-handed mouse).  Set to true to
16740      * allow drag and drop to start with any mouse click that is propogated
16741      * by the browser
16742      * @property primaryButtonOnly
16743      * @type boolean
16744      */
16745     primaryButtonOnly: true,
16746
16747     /**
16748      * The availabe property is false until the linked dom element is accessible.
16749      * @property available
16750      * @type boolean
16751      */
16752     available: false,
16753
16754     /**
16755      * By default, drags can only be initiated if the mousedown occurs in the
16756      * region the linked element is.  This is done in part to work around a
16757      * bug in some browsers that mis-report the mousedown if the previous
16758      * mouseup happened outside of the window.  This property is set to true
16759      * if outer handles are defined.
16760      *
16761      * @property hasOuterHandles
16762      * @type boolean
16763      * @default false
16764      */
16765     hasOuterHandles: false,
16766
16767     /**
16768      * Code that executes immediately before the startDrag event
16769      * @method b4StartDrag
16770      * @private
16771      */
16772     b4StartDrag: function(x, y) { },
16773
16774     /**
16775      * Abstract method called after a drag/drop object is clicked
16776      * and the drag or mousedown time thresholds have beeen met.
16777      * @method startDrag
16778      * @param {int} X click location
16779      * @param {int} Y click location
16780      */
16781     startDrag: function(x, y) { /* override this */ },
16782
16783     /**
16784      * Code that executes immediately before the onDrag event
16785      * @method b4Drag
16786      * @private
16787      */
16788     b4Drag: function(e) { },
16789
16790     /**
16791      * Abstract method called during the onMouseMove event while dragging an
16792      * object.
16793      * @method onDrag
16794      * @param {Event} e the mousemove event
16795      */
16796     onDrag: function(e) { /* override this */ },
16797
16798     /**
16799      * Abstract method called when this element fist begins hovering over
16800      * another DragDrop obj
16801      * @method onDragEnter
16802      * @param {Event} e the mousemove event
16803      * @param {String|DragDrop[]} id In POINT mode, the element
16804      * id this is hovering over.  In INTERSECT mode, an array of one or more
16805      * dragdrop items being hovered over.
16806      */
16807     onDragEnter: function(e, id) { /* override this */ },
16808
16809     /**
16810      * Code that executes immediately before the onDragOver event
16811      * @method b4DragOver
16812      * @private
16813      */
16814     b4DragOver: function(e) { },
16815
16816     /**
16817      * Abstract method called when this element is hovering over another
16818      * DragDrop obj
16819      * @method onDragOver
16820      * @param {Event} e the mousemove event
16821      * @param {String|DragDrop[]} id In POINT mode, the element
16822      * id this is hovering over.  In INTERSECT mode, an array of dd items
16823      * being hovered over.
16824      */
16825     onDragOver: function(e, id) { /* override this */ },
16826
16827     /**
16828      * Code that executes immediately before the onDragOut event
16829      * @method b4DragOut
16830      * @private
16831      */
16832     b4DragOut: function(e) { },
16833
16834     /**
16835      * Abstract method called when we are no longer hovering over an element
16836      * @method onDragOut
16837      * @param {Event} e the mousemove event
16838      * @param {String|DragDrop[]} id In POINT mode, the element
16839      * id this was hovering over.  In INTERSECT mode, an array of dd items
16840      * that the mouse is no longer over.
16841      */
16842     onDragOut: function(e, id) { /* override this */ },
16843
16844     /**
16845      * Code that executes immediately before the onDragDrop event
16846      * @method b4DragDrop
16847      * @private
16848      */
16849     b4DragDrop: function(e) { },
16850
16851     /**
16852      * Abstract method called when this item is dropped on another DragDrop
16853      * obj
16854      * @method onDragDrop
16855      * @param {Event} e the mouseup event
16856      * @param {String|DragDrop[]} id In POINT mode, the element
16857      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16858      * was dropped on.
16859      */
16860     onDragDrop: function(e, id) { /* override this */ },
16861
16862     /**
16863      * Abstract method called when this item is dropped on an area with no
16864      * drop target
16865      * @method onInvalidDrop
16866      * @param {Event} e the mouseup event
16867      */
16868     onInvalidDrop: function(e) { /* override this */ },
16869
16870     /**
16871      * Code that executes immediately before the endDrag event
16872      * @method b4EndDrag
16873      * @private
16874      */
16875     b4EndDrag: function(e) { },
16876
16877     /**
16878      * Fired when we are done dragging the object
16879      * @method endDrag
16880      * @param {Event} e the mouseup event
16881      */
16882     endDrag: function(e) { /* override this */ },
16883
16884     /**
16885      * Code executed immediately before the onMouseDown event
16886      * @method b4MouseDown
16887      * @param {Event} e the mousedown event
16888      * @private
16889      */
16890     b4MouseDown: function(e) {  },
16891
16892     /**
16893      * Event handler that fires when a drag/drop obj gets a mousedown
16894      * @method onMouseDown
16895      * @param {Event} e the mousedown event
16896      */
16897     onMouseDown: function(e) { /* override this */ },
16898
16899     /**
16900      * Event handler that fires when a drag/drop obj gets a mouseup
16901      * @method onMouseUp
16902      * @param {Event} e the mouseup event
16903      */
16904     onMouseUp: function(e) { /* override this */ },
16905
16906     /**
16907      * Override the onAvailable method to do what is needed after the initial
16908      * position was determined.
16909      * @method onAvailable
16910      */
16911     onAvailable: function () {
16912     },
16913
16914     /*
16915      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16916      * @type Object
16917      */
16918     defaultPadding : {left:0, right:0, top:0, bottom:0},
16919
16920     /*
16921      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16922  *
16923  * Usage:
16924  <pre><code>
16925  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16926                 { dragElId: "existingProxyDiv" });
16927  dd.startDrag = function(){
16928      this.constrainTo("parent-id");
16929  };
16930  </code></pre>
16931  * Or you can initalize it using the {@link Roo.Element} object:
16932  <pre><code>
16933  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16934      startDrag : function(){
16935          this.constrainTo("parent-id");
16936      }
16937  });
16938  </code></pre>
16939      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16940      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16941      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16942      * an object containing the sides to pad. For example: {right:10, bottom:10}
16943      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16944      */
16945     constrainTo : function(constrainTo, pad, inContent){
16946         if(typeof pad == "number"){
16947             pad = {left: pad, right:pad, top:pad, bottom:pad};
16948         }
16949         pad = pad || this.defaultPadding;
16950         var b = Roo.get(this.getEl()).getBox();
16951         var ce = Roo.get(constrainTo);
16952         var s = ce.getScroll();
16953         var c, cd = ce.dom;
16954         if(cd == document.body){
16955             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16956         }else{
16957             xy = ce.getXY();
16958             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16959         }
16960
16961
16962         var topSpace = b.y - c.y;
16963         var leftSpace = b.x - c.x;
16964
16965         this.resetConstraints();
16966         this.setXConstraint(leftSpace - (pad.left||0), // left
16967                 c.width - leftSpace - b.width - (pad.right||0) //right
16968         );
16969         this.setYConstraint(topSpace - (pad.top||0), //top
16970                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16971         );
16972     },
16973
16974     /**
16975      * Returns a reference to the linked element
16976      * @method getEl
16977      * @return {HTMLElement} the html element
16978      */
16979     getEl: function() {
16980         if (!this._domRef) {
16981             this._domRef = Roo.getDom(this.id);
16982         }
16983
16984         return this._domRef;
16985     },
16986
16987     /**
16988      * Returns a reference to the actual element to drag.  By default this is
16989      * the same as the html element, but it can be assigned to another
16990      * element. An example of this can be found in Roo.dd.DDProxy
16991      * @method getDragEl
16992      * @return {HTMLElement} the html element
16993      */
16994     getDragEl: function() {
16995         return Roo.getDom(this.dragElId);
16996     },
16997
16998     /**
16999      * Sets up the DragDrop object.  Must be called in the constructor of any
17000      * Roo.dd.DragDrop subclass
17001      * @method init
17002      * @param id the id of the linked element
17003      * @param {String} sGroup the group of related items
17004      * @param {object} config configuration attributes
17005      */
17006     init: function(id, sGroup, config) {
17007         this.initTarget(id, sGroup, config);
17008         if (!Roo.isTouch) {
17009             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17010         }
17011         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17012         // Event.on(this.id, "selectstart", Event.preventDefault);
17013     },
17014
17015     /**
17016      * Initializes Targeting functionality only... the object does not
17017      * get a mousedown handler.
17018      * @method initTarget
17019      * @param id the id of the linked element
17020      * @param {String} sGroup the group of related items
17021      * @param {object} config configuration attributes
17022      */
17023     initTarget: function(id, sGroup, config) {
17024
17025         // configuration attributes
17026         this.config = config || {};
17027
17028         // create a local reference to the drag and drop manager
17029         this.DDM = Roo.dd.DDM;
17030         // initialize the groups array
17031         this.groups = {};
17032
17033         // assume that we have an element reference instead of an id if the
17034         // parameter is not a string
17035         if (typeof id !== "string") {
17036             id = Roo.id(id);
17037         }
17038
17039         // set the id
17040         this.id = id;
17041
17042         // add to an interaction group
17043         this.addToGroup((sGroup) ? sGroup : "default");
17044
17045         // We don't want to register this as the handle with the manager
17046         // so we just set the id rather than calling the setter.
17047         this.handleElId = id;
17048
17049         // the linked element is the element that gets dragged by default
17050         this.setDragElId(id);
17051
17052         // by default, clicked anchors will not start drag operations.
17053         this.invalidHandleTypes = { A: "A" };
17054         this.invalidHandleIds = {};
17055         this.invalidHandleClasses = [];
17056
17057         this.applyConfig();
17058
17059         this.handleOnAvailable();
17060     },
17061
17062     /**
17063      * Applies the configuration parameters that were passed into the constructor.
17064      * This is supposed to happen at each level through the inheritance chain.  So
17065      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17066      * DragDrop in order to get all of the parameters that are available in
17067      * each object.
17068      * @method applyConfig
17069      */
17070     applyConfig: function() {
17071
17072         // configurable properties:
17073         //    padding, isTarget, maintainOffset, primaryButtonOnly
17074         this.padding           = this.config.padding || [0, 0, 0, 0];
17075         this.isTarget          = (this.config.isTarget !== false);
17076         this.maintainOffset    = (this.config.maintainOffset);
17077         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17078
17079     },
17080
17081     /**
17082      * Executed when the linked element is available
17083      * @method handleOnAvailable
17084      * @private
17085      */
17086     handleOnAvailable: function() {
17087         this.available = true;
17088         this.resetConstraints();
17089         this.onAvailable();
17090     },
17091
17092      /**
17093      * Configures the padding for the target zone in px.  Effectively expands
17094      * (or reduces) the virtual object size for targeting calculations.
17095      * Supports css-style shorthand; if only one parameter is passed, all sides
17096      * will have that padding, and if only two are passed, the top and bottom
17097      * will have the first param, the left and right the second.
17098      * @method setPadding
17099      * @param {int} iTop    Top pad
17100      * @param {int} iRight  Right pad
17101      * @param {int} iBot    Bot pad
17102      * @param {int} iLeft   Left pad
17103      */
17104     setPadding: function(iTop, iRight, iBot, iLeft) {
17105         // this.padding = [iLeft, iRight, iTop, iBot];
17106         if (!iRight && 0 !== iRight) {
17107             this.padding = [iTop, iTop, iTop, iTop];
17108         } else if (!iBot && 0 !== iBot) {
17109             this.padding = [iTop, iRight, iTop, iRight];
17110         } else {
17111             this.padding = [iTop, iRight, iBot, iLeft];
17112         }
17113     },
17114
17115     /**
17116      * Stores the initial placement of the linked element.
17117      * @method setInitialPosition
17118      * @param {int} diffX   the X offset, default 0
17119      * @param {int} diffY   the Y offset, default 0
17120      */
17121     setInitPosition: function(diffX, diffY) {
17122         var el = this.getEl();
17123
17124         if (!this.DDM.verifyEl(el)) {
17125             return;
17126         }
17127
17128         var dx = diffX || 0;
17129         var dy = diffY || 0;
17130
17131         var p = Dom.getXY( el );
17132
17133         this.initPageX = p[0] - dx;
17134         this.initPageY = p[1] - dy;
17135
17136         this.lastPageX = p[0];
17137         this.lastPageY = p[1];
17138
17139
17140         this.setStartPosition(p);
17141     },
17142
17143     /**
17144      * Sets the start position of the element.  This is set when the obj
17145      * is initialized, the reset when a drag is started.
17146      * @method setStartPosition
17147      * @param pos current position (from previous lookup)
17148      * @private
17149      */
17150     setStartPosition: function(pos) {
17151         var p = pos || Dom.getXY( this.getEl() );
17152         this.deltaSetXY = null;
17153
17154         this.startPageX = p[0];
17155         this.startPageY = p[1];
17156     },
17157
17158     /**
17159      * Add this instance to a group of related drag/drop objects.  All
17160      * instances belong to at least one group, and can belong to as many
17161      * groups as needed.
17162      * @method addToGroup
17163      * @param sGroup {string} the name of the group
17164      */
17165     addToGroup: function(sGroup) {
17166         this.groups[sGroup] = true;
17167         this.DDM.regDragDrop(this, sGroup);
17168     },
17169
17170     /**
17171      * Remove's this instance from the supplied interaction group
17172      * @method removeFromGroup
17173      * @param {string}  sGroup  The group to drop
17174      */
17175     removeFromGroup: function(sGroup) {
17176         if (this.groups[sGroup]) {
17177             delete this.groups[sGroup];
17178         }
17179
17180         this.DDM.removeDDFromGroup(this, sGroup);
17181     },
17182
17183     /**
17184      * Allows you to specify that an element other than the linked element
17185      * will be moved with the cursor during a drag
17186      * @method setDragElId
17187      * @param id {string} the id of the element that will be used to initiate the drag
17188      */
17189     setDragElId: function(id) {
17190         this.dragElId = id;
17191     },
17192
17193     /**
17194      * Allows you to specify a child of the linked element that should be
17195      * used to initiate the drag operation.  An example of this would be if
17196      * you have a content div with text and links.  Clicking anywhere in the
17197      * content area would normally start the drag operation.  Use this method
17198      * to specify that an element inside of the content div is the element
17199      * that starts the drag operation.
17200      * @method setHandleElId
17201      * @param id {string} the id of the element that will be used to
17202      * initiate the drag.
17203      */
17204     setHandleElId: function(id) {
17205         if (typeof id !== "string") {
17206             id = Roo.id(id);
17207         }
17208         this.handleElId = id;
17209         this.DDM.regHandle(this.id, id);
17210     },
17211
17212     /**
17213      * Allows you to set an element outside of the linked element as a drag
17214      * handle
17215      * @method setOuterHandleElId
17216      * @param id the id of the element that will be used to initiate the drag
17217      */
17218     setOuterHandleElId: function(id) {
17219         if (typeof id !== "string") {
17220             id = Roo.id(id);
17221         }
17222         Event.on(id, "mousedown",
17223                 this.handleMouseDown, this);
17224         this.setHandleElId(id);
17225
17226         this.hasOuterHandles = true;
17227     },
17228
17229     /**
17230      * Remove all drag and drop hooks for this element
17231      * @method unreg
17232      */
17233     unreg: function() {
17234         Event.un(this.id, "mousedown",
17235                 this.handleMouseDown);
17236         Event.un(this.id, "touchstart",
17237                 this.handleMouseDown);
17238         this._domRef = null;
17239         this.DDM._remove(this);
17240     },
17241
17242     destroy : function(){
17243         this.unreg();
17244     },
17245
17246     /**
17247      * Returns true if this instance is locked, or the drag drop mgr is locked
17248      * (meaning that all drag/drop is disabled on the page.)
17249      * @method isLocked
17250      * @return {boolean} true if this obj or all drag/drop is locked, else
17251      * false
17252      */
17253     isLocked: function() {
17254         return (this.DDM.isLocked() || this.locked);
17255     },
17256
17257     /**
17258      * Fired when this object is clicked
17259      * @method handleMouseDown
17260      * @param {Event} e
17261      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17262      * @private
17263      */
17264     handleMouseDown: function(e, oDD){
17265      
17266         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17267             //Roo.log('not touch/ button !=0');
17268             return;
17269         }
17270         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17271             return; // double touch..
17272         }
17273         
17274
17275         if (this.isLocked()) {
17276             //Roo.log('locked');
17277             return;
17278         }
17279
17280         this.DDM.refreshCache(this.groups);
17281 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17282         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17283         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17284             //Roo.log('no outer handes or not over target');
17285                 // do nothing.
17286         } else {
17287 //            Roo.log('check validator');
17288             if (this.clickValidator(e)) {
17289 //                Roo.log('validate success');
17290                 // set the initial element position
17291                 this.setStartPosition();
17292
17293
17294                 this.b4MouseDown(e);
17295                 this.onMouseDown(e);
17296
17297                 this.DDM.handleMouseDown(e, this);
17298
17299                 this.DDM.stopEvent(e);
17300             } else {
17301
17302
17303             }
17304         }
17305     },
17306
17307     clickValidator: function(e) {
17308         var target = e.getTarget();
17309         return ( this.isValidHandleChild(target) &&
17310                     (this.id == this.handleElId ||
17311                         this.DDM.handleWasClicked(target, this.id)) );
17312     },
17313
17314     /**
17315      * Allows you to specify a tag name that should not start a drag operation
17316      * when clicked.  This is designed to facilitate embedding links within a
17317      * drag handle that do something other than start the drag.
17318      * @method addInvalidHandleType
17319      * @param {string} tagName the type of element to exclude
17320      */
17321     addInvalidHandleType: function(tagName) {
17322         var type = tagName.toUpperCase();
17323         this.invalidHandleTypes[type] = type;
17324     },
17325
17326     /**
17327      * Lets you to specify an element id for a child of a drag handle
17328      * that should not initiate a drag
17329      * @method addInvalidHandleId
17330      * @param {string} id the element id of the element you wish to ignore
17331      */
17332     addInvalidHandleId: function(id) {
17333         if (typeof id !== "string") {
17334             id = Roo.id(id);
17335         }
17336         this.invalidHandleIds[id] = id;
17337     },
17338
17339     /**
17340      * Lets you specify a css class of elements that will not initiate a drag
17341      * @method addInvalidHandleClass
17342      * @param {string} cssClass the class of the elements you wish to ignore
17343      */
17344     addInvalidHandleClass: function(cssClass) {
17345         this.invalidHandleClasses.push(cssClass);
17346     },
17347
17348     /**
17349      * Unsets an excluded tag name set by addInvalidHandleType
17350      * @method removeInvalidHandleType
17351      * @param {string} tagName the type of element to unexclude
17352      */
17353     removeInvalidHandleType: function(tagName) {
17354         var type = tagName.toUpperCase();
17355         // this.invalidHandleTypes[type] = null;
17356         delete this.invalidHandleTypes[type];
17357     },
17358
17359     /**
17360      * Unsets an invalid handle id
17361      * @method removeInvalidHandleId
17362      * @param {string} id the id of the element to re-enable
17363      */
17364     removeInvalidHandleId: function(id) {
17365         if (typeof id !== "string") {
17366             id = Roo.id(id);
17367         }
17368         delete this.invalidHandleIds[id];
17369     },
17370
17371     /**
17372      * Unsets an invalid css class
17373      * @method removeInvalidHandleClass
17374      * @param {string} cssClass the class of the element(s) you wish to
17375      * re-enable
17376      */
17377     removeInvalidHandleClass: function(cssClass) {
17378         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17379             if (this.invalidHandleClasses[i] == cssClass) {
17380                 delete this.invalidHandleClasses[i];
17381             }
17382         }
17383     },
17384
17385     /**
17386      * Checks the tag exclusion list to see if this click should be ignored
17387      * @method isValidHandleChild
17388      * @param {HTMLElement} node the HTMLElement to evaluate
17389      * @return {boolean} true if this is a valid tag type, false if not
17390      */
17391     isValidHandleChild: function(node) {
17392
17393         var valid = true;
17394         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17395         var nodeName;
17396         try {
17397             nodeName = node.nodeName.toUpperCase();
17398         } catch(e) {
17399             nodeName = node.nodeName;
17400         }
17401         valid = valid && !this.invalidHandleTypes[nodeName];
17402         valid = valid && !this.invalidHandleIds[node.id];
17403
17404         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17405             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17406         }
17407
17408
17409         return valid;
17410
17411     },
17412
17413     /**
17414      * Create the array of horizontal tick marks if an interval was specified
17415      * in setXConstraint().
17416      * @method setXTicks
17417      * @private
17418      */
17419     setXTicks: function(iStartX, iTickSize) {
17420         this.xTicks = [];
17421         this.xTickSize = iTickSize;
17422
17423         var tickMap = {};
17424
17425         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17426             if (!tickMap[i]) {
17427                 this.xTicks[this.xTicks.length] = i;
17428                 tickMap[i] = true;
17429             }
17430         }
17431
17432         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17433             if (!tickMap[i]) {
17434                 this.xTicks[this.xTicks.length] = i;
17435                 tickMap[i] = true;
17436             }
17437         }
17438
17439         this.xTicks.sort(this.DDM.numericSort) ;
17440     },
17441
17442     /**
17443      * Create the array of vertical tick marks if an interval was specified in
17444      * setYConstraint().
17445      * @method setYTicks
17446      * @private
17447      */
17448     setYTicks: function(iStartY, iTickSize) {
17449         this.yTicks = [];
17450         this.yTickSize = iTickSize;
17451
17452         var tickMap = {};
17453
17454         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17455             if (!tickMap[i]) {
17456                 this.yTicks[this.yTicks.length] = i;
17457                 tickMap[i] = true;
17458             }
17459         }
17460
17461         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17462             if (!tickMap[i]) {
17463                 this.yTicks[this.yTicks.length] = i;
17464                 tickMap[i] = true;
17465             }
17466         }
17467
17468         this.yTicks.sort(this.DDM.numericSort) ;
17469     },
17470
17471     /**
17472      * By default, the element can be dragged any place on the screen.  Use
17473      * this method to limit the horizontal travel of the element.  Pass in
17474      * 0,0 for the parameters if you want to lock the drag to the y axis.
17475      * @method setXConstraint
17476      * @param {int} iLeft the number of pixels the element can move to the left
17477      * @param {int} iRight the number of pixels the element can move to the
17478      * right
17479      * @param {int} iTickSize optional parameter for specifying that the
17480      * element
17481      * should move iTickSize pixels at a time.
17482      */
17483     setXConstraint: function(iLeft, iRight, iTickSize) {
17484         this.leftConstraint = iLeft;
17485         this.rightConstraint = iRight;
17486
17487         this.minX = this.initPageX - iLeft;
17488         this.maxX = this.initPageX + iRight;
17489         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17490
17491         this.constrainX = true;
17492     },
17493
17494     /**
17495      * Clears any constraints applied to this instance.  Also clears ticks
17496      * since they can't exist independent of a constraint at this time.
17497      * @method clearConstraints
17498      */
17499     clearConstraints: function() {
17500         this.constrainX = false;
17501         this.constrainY = false;
17502         this.clearTicks();
17503     },
17504
17505     /**
17506      * Clears any tick interval defined for this instance
17507      * @method clearTicks
17508      */
17509     clearTicks: function() {
17510         this.xTicks = null;
17511         this.yTicks = null;
17512         this.xTickSize = 0;
17513         this.yTickSize = 0;
17514     },
17515
17516     /**
17517      * By default, the element can be dragged any place on the screen.  Set
17518      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17519      * parameters if you want to lock the drag to the x axis.
17520      * @method setYConstraint
17521      * @param {int} iUp the number of pixels the element can move up
17522      * @param {int} iDown the number of pixels the element can move down
17523      * @param {int} iTickSize optional parameter for specifying that the
17524      * element should move iTickSize pixels at a time.
17525      */
17526     setYConstraint: function(iUp, iDown, iTickSize) {
17527         this.topConstraint = iUp;
17528         this.bottomConstraint = iDown;
17529
17530         this.minY = this.initPageY - iUp;
17531         this.maxY = this.initPageY + iDown;
17532         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17533
17534         this.constrainY = true;
17535
17536     },
17537
17538     /**
17539      * resetConstraints must be called if you manually reposition a dd element.
17540      * @method resetConstraints
17541      * @param {boolean} maintainOffset
17542      */
17543     resetConstraints: function() {
17544
17545
17546         // Maintain offsets if necessary
17547         if (this.initPageX || this.initPageX === 0) {
17548             // figure out how much this thing has moved
17549             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17550             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17551
17552             this.setInitPosition(dx, dy);
17553
17554         // This is the first time we have detected the element's position
17555         } else {
17556             this.setInitPosition();
17557         }
17558
17559         if (this.constrainX) {
17560             this.setXConstraint( this.leftConstraint,
17561                                  this.rightConstraint,
17562                                  this.xTickSize        );
17563         }
17564
17565         if (this.constrainY) {
17566             this.setYConstraint( this.topConstraint,
17567                                  this.bottomConstraint,
17568                                  this.yTickSize         );
17569         }
17570     },
17571
17572     /**
17573      * Normally the drag element is moved pixel by pixel, but we can specify
17574      * that it move a number of pixels at a time.  This method resolves the
17575      * location when we have it set up like this.
17576      * @method getTick
17577      * @param {int} val where we want to place the object
17578      * @param {int[]} tickArray sorted array of valid points
17579      * @return {int} the closest tick
17580      * @private
17581      */
17582     getTick: function(val, tickArray) {
17583
17584         if (!tickArray) {
17585             // If tick interval is not defined, it is effectively 1 pixel,
17586             // so we return the value passed to us.
17587             return val;
17588         } else if (tickArray[0] >= val) {
17589             // The value is lower than the first tick, so we return the first
17590             // tick.
17591             return tickArray[0];
17592         } else {
17593             for (var i=0, len=tickArray.length; i<len; ++i) {
17594                 var next = i + 1;
17595                 if (tickArray[next] && tickArray[next] >= val) {
17596                     var diff1 = val - tickArray[i];
17597                     var diff2 = tickArray[next] - val;
17598                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17599                 }
17600             }
17601
17602             // The value is larger than the last tick, so we return the last
17603             // tick.
17604             return tickArray[tickArray.length - 1];
17605         }
17606     },
17607
17608     /**
17609      * toString method
17610      * @method toString
17611      * @return {string} string representation of the dd obj
17612      */
17613     toString: function() {
17614         return ("DragDrop " + this.id);
17615     }
17616
17617 });
17618
17619 })();
17620 /*
17621  * Based on:
17622  * Ext JS Library 1.1.1
17623  * Copyright(c) 2006-2007, Ext JS, LLC.
17624  *
17625  * Originally Released Under LGPL - original licence link has changed is not relivant.
17626  *
17627  * Fork - LGPL
17628  * <script type="text/javascript">
17629  */
17630
17631
17632 /**
17633  * The drag and drop utility provides a framework for building drag and drop
17634  * applications.  In addition to enabling drag and drop for specific elements,
17635  * the drag and drop elements are tracked by the manager class, and the
17636  * interactions between the various elements are tracked during the drag and
17637  * the implementing code is notified about these important moments.
17638  */
17639
17640 // Only load the library once.  Rewriting the manager class would orphan
17641 // existing drag and drop instances.
17642 if (!Roo.dd.DragDropMgr) {
17643
17644 /**
17645  * @class Roo.dd.DragDropMgr
17646  * DragDropMgr is a singleton that tracks the element interaction for
17647  * all DragDrop items in the window.  Generally, you will not call
17648  * this class directly, but it does have helper methods that could
17649  * be useful in your DragDrop implementations.
17650  * @singleton
17651  */
17652 Roo.dd.DragDropMgr = function() {
17653
17654     var Event = Roo.EventManager;
17655
17656     return {
17657
17658         /**
17659          * Two dimensional Array of registered DragDrop objects.  The first
17660          * dimension is the DragDrop item group, the second the DragDrop
17661          * object.
17662          * @property ids
17663          * @type {string: string}
17664          * @private
17665          * @static
17666          */
17667         ids: {},
17668
17669         /**
17670          * Array of element ids defined as drag handles.  Used to determine
17671          * if the element that generated the mousedown event is actually the
17672          * handle and not the html element itself.
17673          * @property handleIds
17674          * @type {string: string}
17675          * @private
17676          * @static
17677          */
17678         handleIds: {},
17679
17680         /**
17681          * the DragDrop object that is currently being dragged
17682          * @property dragCurrent
17683          * @type DragDrop
17684          * @private
17685          * @static
17686          **/
17687         dragCurrent: null,
17688
17689         /**
17690          * the DragDrop object(s) that are being hovered over
17691          * @property dragOvers
17692          * @type Array
17693          * @private
17694          * @static
17695          */
17696         dragOvers: {},
17697
17698         /**
17699          * the X distance between the cursor and the object being dragged
17700          * @property deltaX
17701          * @type int
17702          * @private
17703          * @static
17704          */
17705         deltaX: 0,
17706
17707         /**
17708          * the Y distance between the cursor and the object being dragged
17709          * @property deltaY
17710          * @type int
17711          * @private
17712          * @static
17713          */
17714         deltaY: 0,
17715
17716         /**
17717          * Flag to determine if we should prevent the default behavior of the
17718          * events we define. By default this is true, but this can be set to
17719          * false if you need the default behavior (not recommended)
17720          * @property preventDefault
17721          * @type boolean
17722          * @static
17723          */
17724         preventDefault: true,
17725
17726         /**
17727          * Flag to determine if we should stop the propagation of the events
17728          * we generate. This is true by default but you may want to set it to
17729          * false if the html element contains other features that require the
17730          * mouse click.
17731          * @property stopPropagation
17732          * @type boolean
17733          * @static
17734          */
17735         stopPropagation: true,
17736
17737         /**
17738          * Internal flag that is set to true when drag and drop has been
17739          * intialized
17740          * @property initialized
17741          * @private
17742          * @static
17743          */
17744         initalized: false,
17745
17746         /**
17747          * All drag and drop can be disabled.
17748          * @property locked
17749          * @private
17750          * @static
17751          */
17752         locked: false,
17753
17754         /**
17755          * Called the first time an element is registered.
17756          * @method init
17757          * @private
17758          * @static
17759          */
17760         init: function() {
17761             this.initialized = true;
17762         },
17763
17764         /**
17765          * In point mode, drag and drop interaction is defined by the
17766          * location of the cursor during the drag/drop
17767          * @property POINT
17768          * @type int
17769          * @static
17770          */
17771         POINT: 0,
17772
17773         /**
17774          * In intersect mode, drag and drop interactio nis defined by the
17775          * overlap of two or more drag and drop objects.
17776          * @property INTERSECT
17777          * @type int
17778          * @static
17779          */
17780         INTERSECT: 1,
17781
17782         /**
17783          * The current drag and drop mode.  Default: POINT
17784          * @property mode
17785          * @type int
17786          * @static
17787          */
17788         mode: 0,
17789
17790         /**
17791          * Runs method on all drag and drop objects
17792          * @method _execOnAll
17793          * @private
17794          * @static
17795          */
17796         _execOnAll: function(sMethod, args) {
17797             for (var i in this.ids) {
17798                 for (var j in this.ids[i]) {
17799                     var oDD = this.ids[i][j];
17800                     if (! this.isTypeOfDD(oDD)) {
17801                         continue;
17802                     }
17803                     oDD[sMethod].apply(oDD, args);
17804                 }
17805             }
17806         },
17807
17808         /**
17809          * Drag and drop initialization.  Sets up the global event handlers
17810          * @method _onLoad
17811          * @private
17812          * @static
17813          */
17814         _onLoad: function() {
17815
17816             this.init();
17817
17818             if (!Roo.isTouch) {
17819                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17820                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17821             }
17822             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17823             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17824             
17825             Event.on(window,   "unload",    this._onUnload, this, true);
17826             Event.on(window,   "resize",    this._onResize, this, true);
17827             // Event.on(window,   "mouseout",    this._test);
17828
17829         },
17830
17831         /**
17832          * Reset constraints on all drag and drop objs
17833          * @method _onResize
17834          * @private
17835          * @static
17836          */
17837         _onResize: function(e) {
17838             this._execOnAll("resetConstraints", []);
17839         },
17840
17841         /**
17842          * Lock all drag and drop functionality
17843          * @method lock
17844          * @static
17845          */
17846         lock: function() { this.locked = true; },
17847
17848         /**
17849          * Unlock all drag and drop functionality
17850          * @method unlock
17851          * @static
17852          */
17853         unlock: function() { this.locked = false; },
17854
17855         /**
17856          * Is drag and drop locked?
17857          * @method isLocked
17858          * @return {boolean} True if drag and drop is locked, false otherwise.
17859          * @static
17860          */
17861         isLocked: function() { return this.locked; },
17862
17863         /**
17864          * Location cache that is set for all drag drop objects when a drag is
17865          * initiated, cleared when the drag is finished.
17866          * @property locationCache
17867          * @private
17868          * @static
17869          */
17870         locationCache: {},
17871
17872         /**
17873          * Set useCache to false if you want to force object the lookup of each
17874          * drag and drop linked element constantly during a drag.
17875          * @property useCache
17876          * @type boolean
17877          * @static
17878          */
17879         useCache: true,
17880
17881         /**
17882          * The number of pixels that the mouse needs to move after the
17883          * mousedown before the drag is initiated.  Default=3;
17884          * @property clickPixelThresh
17885          * @type int
17886          * @static
17887          */
17888         clickPixelThresh: 3,
17889
17890         /**
17891          * The number of milliseconds after the mousedown event to initiate the
17892          * drag if we don't get a mouseup event. Default=1000
17893          * @property clickTimeThresh
17894          * @type int
17895          * @static
17896          */
17897         clickTimeThresh: 350,
17898
17899         /**
17900          * Flag that indicates that either the drag pixel threshold or the
17901          * mousdown time threshold has been met
17902          * @property dragThreshMet
17903          * @type boolean
17904          * @private
17905          * @static
17906          */
17907         dragThreshMet: false,
17908
17909         /**
17910          * Timeout used for the click time threshold
17911          * @property clickTimeout
17912          * @type Object
17913          * @private
17914          * @static
17915          */
17916         clickTimeout: null,
17917
17918         /**
17919          * The X position of the mousedown event stored for later use when a
17920          * drag threshold is met.
17921          * @property startX
17922          * @type int
17923          * @private
17924          * @static
17925          */
17926         startX: 0,
17927
17928         /**
17929          * The Y position of the mousedown event stored for later use when a
17930          * drag threshold is met.
17931          * @property startY
17932          * @type int
17933          * @private
17934          * @static
17935          */
17936         startY: 0,
17937
17938         /**
17939          * Each DragDrop instance must be registered with the DragDropMgr.
17940          * This is executed in DragDrop.init()
17941          * @method regDragDrop
17942          * @param {DragDrop} oDD the DragDrop object to register
17943          * @param {String} sGroup the name of the group this element belongs to
17944          * @static
17945          */
17946         regDragDrop: function(oDD, sGroup) {
17947             if (!this.initialized) { this.init(); }
17948
17949             if (!this.ids[sGroup]) {
17950                 this.ids[sGroup] = {};
17951             }
17952             this.ids[sGroup][oDD.id] = oDD;
17953         },
17954
17955         /**
17956          * Removes the supplied dd instance from the supplied group. Executed
17957          * by DragDrop.removeFromGroup, so don't call this function directly.
17958          * @method removeDDFromGroup
17959          * @private
17960          * @static
17961          */
17962         removeDDFromGroup: function(oDD, sGroup) {
17963             if (!this.ids[sGroup]) {
17964                 this.ids[sGroup] = {};
17965             }
17966
17967             var obj = this.ids[sGroup];
17968             if (obj && obj[oDD.id]) {
17969                 delete obj[oDD.id];
17970             }
17971         },
17972
17973         /**
17974          * Unregisters a drag and drop item.  This is executed in
17975          * DragDrop.unreg, use that method instead of calling this directly.
17976          * @method _remove
17977          * @private
17978          * @static
17979          */
17980         _remove: function(oDD) {
17981             for (var g in oDD.groups) {
17982                 if (g && this.ids[g][oDD.id]) {
17983                     delete this.ids[g][oDD.id];
17984                 }
17985             }
17986             delete this.handleIds[oDD.id];
17987         },
17988
17989         /**
17990          * Each DragDrop handle element must be registered.  This is done
17991          * automatically when executing DragDrop.setHandleElId()
17992          * @method regHandle
17993          * @param {String} sDDId the DragDrop id this element is a handle for
17994          * @param {String} sHandleId the id of the element that is the drag
17995          * handle
17996          * @static
17997          */
17998         regHandle: function(sDDId, sHandleId) {
17999             if (!this.handleIds[sDDId]) {
18000                 this.handleIds[sDDId] = {};
18001             }
18002             this.handleIds[sDDId][sHandleId] = sHandleId;
18003         },
18004
18005         /**
18006          * Utility function to determine if a given element has been
18007          * registered as a drag drop item.
18008          * @method isDragDrop
18009          * @param {String} id the element id to check
18010          * @return {boolean} true if this element is a DragDrop item,
18011          * false otherwise
18012          * @static
18013          */
18014         isDragDrop: function(id) {
18015             return ( this.getDDById(id) ) ? true : false;
18016         },
18017
18018         /**
18019          * Returns the drag and drop instances that are in all groups the
18020          * passed in instance belongs to.
18021          * @method getRelated
18022          * @param {DragDrop} p_oDD the obj to get related data for
18023          * @param {boolean} bTargetsOnly if true, only return targetable objs
18024          * @return {DragDrop[]} the related instances
18025          * @static
18026          */
18027         getRelated: function(p_oDD, bTargetsOnly) {
18028             var oDDs = [];
18029             for (var i in p_oDD.groups) {
18030                 for (j in this.ids[i]) {
18031                     var dd = this.ids[i][j];
18032                     if (! this.isTypeOfDD(dd)) {
18033                         continue;
18034                     }
18035                     if (!bTargetsOnly || dd.isTarget) {
18036                         oDDs[oDDs.length] = dd;
18037                     }
18038                 }
18039             }
18040
18041             return oDDs;
18042         },
18043
18044         /**
18045          * Returns true if the specified dd target is a legal target for
18046          * the specifice drag obj
18047          * @method isLegalTarget
18048          * @param {DragDrop} the drag obj
18049          * @param {DragDrop} the target
18050          * @return {boolean} true if the target is a legal target for the
18051          * dd obj
18052          * @static
18053          */
18054         isLegalTarget: function (oDD, oTargetDD) {
18055             var targets = this.getRelated(oDD, true);
18056             for (var i=0, len=targets.length;i<len;++i) {
18057                 if (targets[i].id == oTargetDD.id) {
18058                     return true;
18059                 }
18060             }
18061
18062             return false;
18063         },
18064
18065         /**
18066          * My goal is to be able to transparently determine if an object is
18067          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18068          * returns "object", oDD.constructor.toString() always returns
18069          * "DragDrop" and not the name of the subclass.  So for now it just
18070          * evaluates a well-known variable in DragDrop.
18071          * @method isTypeOfDD
18072          * @param {Object} the object to evaluate
18073          * @return {boolean} true if typeof oDD = DragDrop
18074          * @static
18075          */
18076         isTypeOfDD: function (oDD) {
18077             return (oDD && oDD.__ygDragDrop);
18078         },
18079
18080         /**
18081          * Utility function to determine if a given element has been
18082          * registered as a drag drop handle for the given Drag Drop object.
18083          * @method isHandle
18084          * @param {String} id the element id to check
18085          * @return {boolean} true if this element is a DragDrop handle, false
18086          * otherwise
18087          * @static
18088          */
18089         isHandle: function(sDDId, sHandleId) {
18090             return ( this.handleIds[sDDId] &&
18091                             this.handleIds[sDDId][sHandleId] );
18092         },
18093
18094         /**
18095          * Returns the DragDrop instance for a given id
18096          * @method getDDById
18097          * @param {String} id the id of the DragDrop object
18098          * @return {DragDrop} the drag drop object, null if it is not found
18099          * @static
18100          */
18101         getDDById: function(id) {
18102             for (var i in this.ids) {
18103                 if (this.ids[i][id]) {
18104                     return this.ids[i][id];
18105                 }
18106             }
18107             return null;
18108         },
18109
18110         /**
18111          * Fired after a registered DragDrop object gets the mousedown event.
18112          * Sets up the events required to track the object being dragged
18113          * @method handleMouseDown
18114          * @param {Event} e the event
18115          * @param oDD the DragDrop object being dragged
18116          * @private
18117          * @static
18118          */
18119         handleMouseDown: function(e, oDD) {
18120             if(Roo.QuickTips){
18121                 Roo.QuickTips.disable();
18122             }
18123             this.currentTarget = e.getTarget();
18124
18125             this.dragCurrent = oDD;
18126
18127             var el = oDD.getEl();
18128
18129             // track start position
18130             this.startX = e.getPageX();
18131             this.startY = e.getPageY();
18132
18133             this.deltaX = this.startX - el.offsetLeft;
18134             this.deltaY = this.startY - el.offsetTop;
18135
18136             this.dragThreshMet = false;
18137
18138             this.clickTimeout = setTimeout(
18139                     function() {
18140                         var DDM = Roo.dd.DDM;
18141                         DDM.startDrag(DDM.startX, DDM.startY);
18142                     },
18143                     this.clickTimeThresh );
18144         },
18145
18146         /**
18147          * Fired when either the drag pixel threshol or the mousedown hold
18148          * time threshold has been met.
18149          * @method startDrag
18150          * @param x {int} the X position of the original mousedown
18151          * @param y {int} the Y position of the original mousedown
18152          * @static
18153          */
18154         startDrag: function(x, y) {
18155             clearTimeout(this.clickTimeout);
18156             if (this.dragCurrent) {
18157                 this.dragCurrent.b4StartDrag(x, y);
18158                 this.dragCurrent.startDrag(x, y);
18159             }
18160             this.dragThreshMet = true;
18161         },
18162
18163         /**
18164          * Internal function to handle the mouseup event.  Will be invoked
18165          * from the context of the document.
18166          * @method handleMouseUp
18167          * @param {Event} e the event
18168          * @private
18169          * @static
18170          */
18171         handleMouseUp: function(e) {
18172
18173             if(Roo.QuickTips){
18174                 Roo.QuickTips.enable();
18175             }
18176             if (! this.dragCurrent) {
18177                 return;
18178             }
18179
18180             clearTimeout(this.clickTimeout);
18181
18182             if (this.dragThreshMet) {
18183                 this.fireEvents(e, true);
18184             } else {
18185             }
18186
18187             this.stopDrag(e);
18188
18189             this.stopEvent(e);
18190         },
18191
18192         /**
18193          * Utility to stop event propagation and event default, if these
18194          * features are turned on.
18195          * @method stopEvent
18196          * @param {Event} e the event as returned by this.getEvent()
18197          * @static
18198          */
18199         stopEvent: function(e){
18200             if(this.stopPropagation) {
18201                 e.stopPropagation();
18202             }
18203
18204             if (this.preventDefault) {
18205                 e.preventDefault();
18206             }
18207         },
18208
18209         /**
18210          * Internal function to clean up event handlers after the drag
18211          * operation is complete
18212          * @method stopDrag
18213          * @param {Event} e the event
18214          * @private
18215          * @static
18216          */
18217         stopDrag: function(e) {
18218             // Fire the drag end event for the item that was dragged
18219             if (this.dragCurrent) {
18220                 if (this.dragThreshMet) {
18221                     this.dragCurrent.b4EndDrag(e);
18222                     this.dragCurrent.endDrag(e);
18223                 }
18224
18225                 this.dragCurrent.onMouseUp(e);
18226             }
18227
18228             this.dragCurrent = null;
18229             this.dragOvers = {};
18230         },
18231
18232         /**
18233          * Internal function to handle the mousemove event.  Will be invoked
18234          * from the context of the html element.
18235          *
18236          * @TODO figure out what we can do about mouse events lost when the
18237          * user drags objects beyond the window boundary.  Currently we can
18238          * detect this in internet explorer by verifying that the mouse is
18239          * down during the mousemove event.  Firefox doesn't give us the
18240          * button state on the mousemove event.
18241          * @method handleMouseMove
18242          * @param {Event} e the event
18243          * @private
18244          * @static
18245          */
18246         handleMouseMove: function(e) {
18247             if (! this.dragCurrent) {
18248                 return true;
18249             }
18250
18251             // var button = e.which || e.button;
18252
18253             // check for IE mouseup outside of page boundary
18254             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18255                 this.stopEvent(e);
18256                 return this.handleMouseUp(e);
18257             }
18258
18259             if (!this.dragThreshMet) {
18260                 var diffX = Math.abs(this.startX - e.getPageX());
18261                 var diffY = Math.abs(this.startY - e.getPageY());
18262                 if (diffX > this.clickPixelThresh ||
18263                             diffY > this.clickPixelThresh) {
18264                     this.startDrag(this.startX, this.startY);
18265                 }
18266             }
18267
18268             if (this.dragThreshMet) {
18269                 this.dragCurrent.b4Drag(e);
18270                 this.dragCurrent.onDrag(e);
18271                 if(!this.dragCurrent.moveOnly){
18272                     this.fireEvents(e, false);
18273                 }
18274             }
18275
18276             this.stopEvent(e);
18277
18278             return true;
18279         },
18280
18281         /**
18282          * Iterates over all of the DragDrop elements to find ones we are
18283          * hovering over or dropping on
18284          * @method fireEvents
18285          * @param {Event} e the event
18286          * @param {boolean} isDrop is this a drop op or a mouseover op?
18287          * @private
18288          * @static
18289          */
18290         fireEvents: function(e, isDrop) {
18291             var dc = this.dragCurrent;
18292
18293             // If the user did the mouse up outside of the window, we could
18294             // get here even though we have ended the drag.
18295             if (!dc || dc.isLocked()) {
18296                 return;
18297             }
18298
18299             var pt = e.getPoint();
18300
18301             // cache the previous dragOver array
18302             var oldOvers = [];
18303
18304             var outEvts   = [];
18305             var overEvts  = [];
18306             var dropEvts  = [];
18307             var enterEvts = [];
18308
18309             // Check to see if the object(s) we were hovering over is no longer
18310             // being hovered over so we can fire the onDragOut event
18311             for (var i in this.dragOvers) {
18312
18313                 var ddo = this.dragOvers[i];
18314
18315                 if (! this.isTypeOfDD(ddo)) {
18316                     continue;
18317                 }
18318
18319                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18320                     outEvts.push( ddo );
18321                 }
18322
18323                 oldOvers[i] = true;
18324                 delete this.dragOvers[i];
18325             }
18326
18327             for (var sGroup in dc.groups) {
18328
18329                 if ("string" != typeof sGroup) {
18330                     continue;
18331                 }
18332
18333                 for (i in this.ids[sGroup]) {
18334                     var oDD = this.ids[sGroup][i];
18335                     if (! this.isTypeOfDD(oDD)) {
18336                         continue;
18337                     }
18338
18339                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18340                         if (this.isOverTarget(pt, oDD, this.mode)) {
18341                             // look for drop interactions
18342                             if (isDrop) {
18343                                 dropEvts.push( oDD );
18344                             // look for drag enter and drag over interactions
18345                             } else {
18346
18347                                 // initial drag over: dragEnter fires
18348                                 if (!oldOvers[oDD.id]) {
18349                                     enterEvts.push( oDD );
18350                                 // subsequent drag overs: dragOver fires
18351                                 } else {
18352                                     overEvts.push( oDD );
18353                                 }
18354
18355                                 this.dragOvers[oDD.id] = oDD;
18356                             }
18357                         }
18358                     }
18359                 }
18360             }
18361
18362             if (this.mode) {
18363                 if (outEvts.length) {
18364                     dc.b4DragOut(e, outEvts);
18365                     dc.onDragOut(e, outEvts);
18366                 }
18367
18368                 if (enterEvts.length) {
18369                     dc.onDragEnter(e, enterEvts);
18370                 }
18371
18372                 if (overEvts.length) {
18373                     dc.b4DragOver(e, overEvts);
18374                     dc.onDragOver(e, overEvts);
18375                 }
18376
18377                 if (dropEvts.length) {
18378                     dc.b4DragDrop(e, dropEvts);
18379                     dc.onDragDrop(e, dropEvts);
18380                 }
18381
18382             } else {
18383                 // fire dragout events
18384                 var len = 0;
18385                 for (i=0, len=outEvts.length; i<len; ++i) {
18386                     dc.b4DragOut(e, outEvts[i].id);
18387                     dc.onDragOut(e, outEvts[i].id);
18388                 }
18389
18390                 // fire enter events
18391                 for (i=0,len=enterEvts.length; i<len; ++i) {
18392                     // dc.b4DragEnter(e, oDD.id);
18393                     dc.onDragEnter(e, enterEvts[i].id);
18394                 }
18395
18396                 // fire over events
18397                 for (i=0,len=overEvts.length; i<len; ++i) {
18398                     dc.b4DragOver(e, overEvts[i].id);
18399                     dc.onDragOver(e, overEvts[i].id);
18400                 }
18401
18402                 // fire drop events
18403                 for (i=0, len=dropEvts.length; i<len; ++i) {
18404                     dc.b4DragDrop(e, dropEvts[i].id);
18405                     dc.onDragDrop(e, dropEvts[i].id);
18406                 }
18407
18408             }
18409
18410             // notify about a drop that did not find a target
18411             if (isDrop && !dropEvts.length) {
18412                 dc.onInvalidDrop(e);
18413             }
18414
18415         },
18416
18417         /**
18418          * Helper function for getting the best match from the list of drag
18419          * and drop objects returned by the drag and drop events when we are
18420          * in INTERSECT mode.  It returns either the first object that the
18421          * cursor is over, or the object that has the greatest overlap with
18422          * the dragged element.
18423          * @method getBestMatch
18424          * @param  {DragDrop[]} dds The array of drag and drop objects
18425          * targeted
18426          * @return {DragDrop}       The best single match
18427          * @static
18428          */
18429         getBestMatch: function(dds) {
18430             var winner = null;
18431             // Return null if the input is not what we expect
18432             //if (!dds || !dds.length || dds.length == 0) {
18433                // winner = null;
18434             // If there is only one item, it wins
18435             //} else if (dds.length == 1) {
18436
18437             var len = dds.length;
18438
18439             if (len == 1) {
18440                 winner = dds[0];
18441             } else {
18442                 // Loop through the targeted items
18443                 for (var i=0; i<len; ++i) {
18444                     var dd = dds[i];
18445                     // If the cursor is over the object, it wins.  If the
18446                     // cursor is over multiple matches, the first one we come
18447                     // to wins.
18448                     if (dd.cursorIsOver) {
18449                         winner = dd;
18450                         break;
18451                     // Otherwise the object with the most overlap wins
18452                     } else {
18453                         if (!winner ||
18454                             winner.overlap.getArea() < dd.overlap.getArea()) {
18455                             winner = dd;
18456                         }
18457                     }
18458                 }
18459             }
18460
18461             return winner;
18462         },
18463
18464         /**
18465          * Refreshes the cache of the top-left and bottom-right points of the
18466          * drag and drop objects in the specified group(s).  This is in the
18467          * format that is stored in the drag and drop instance, so typical
18468          * usage is:
18469          * <code>
18470          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18471          * </code>
18472          * Alternatively:
18473          * <code>
18474          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18475          * </code>
18476          * @TODO this really should be an indexed array.  Alternatively this
18477          * method could accept both.
18478          * @method refreshCache
18479          * @param {Object} groups an associative array of groups to refresh
18480          * @static
18481          */
18482         refreshCache: function(groups) {
18483             for (var sGroup in groups) {
18484                 if ("string" != typeof sGroup) {
18485                     continue;
18486                 }
18487                 for (var i in this.ids[sGroup]) {
18488                     var oDD = this.ids[sGroup][i];
18489
18490                     if (this.isTypeOfDD(oDD)) {
18491                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18492                         var loc = this.getLocation(oDD);
18493                         if (loc) {
18494                             this.locationCache[oDD.id] = loc;
18495                         } else {
18496                             delete this.locationCache[oDD.id];
18497                             // this will unregister the drag and drop object if
18498                             // the element is not in a usable state
18499                             // oDD.unreg();
18500                         }
18501                     }
18502                 }
18503             }
18504         },
18505
18506         /**
18507          * This checks to make sure an element exists and is in the DOM.  The
18508          * main purpose is to handle cases where innerHTML is used to remove
18509          * drag and drop objects from the DOM.  IE provides an 'unspecified
18510          * error' when trying to access the offsetParent of such an element
18511          * @method verifyEl
18512          * @param {HTMLElement} el the element to check
18513          * @return {boolean} true if the element looks usable
18514          * @static
18515          */
18516         verifyEl: function(el) {
18517             if (el) {
18518                 var parent;
18519                 if(Roo.isIE){
18520                     try{
18521                         parent = el.offsetParent;
18522                     }catch(e){}
18523                 }else{
18524                     parent = el.offsetParent;
18525                 }
18526                 if (parent) {
18527                     return true;
18528                 }
18529             }
18530
18531             return false;
18532         },
18533
18534         /**
18535          * Returns a Region object containing the drag and drop element's position
18536          * and size, including the padding configured for it
18537          * @method getLocation
18538          * @param {DragDrop} oDD the drag and drop object to get the
18539          *                       location for
18540          * @return {Roo.lib.Region} a Region object representing the total area
18541          *                             the element occupies, including any padding
18542          *                             the instance is configured for.
18543          * @static
18544          */
18545         getLocation: function(oDD) {
18546             if (! this.isTypeOfDD(oDD)) {
18547                 return null;
18548             }
18549
18550             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18551
18552             try {
18553                 pos= Roo.lib.Dom.getXY(el);
18554             } catch (e) { }
18555
18556             if (!pos) {
18557                 return null;
18558             }
18559
18560             x1 = pos[0];
18561             x2 = x1 + el.offsetWidth;
18562             y1 = pos[1];
18563             y2 = y1 + el.offsetHeight;
18564
18565             t = y1 - oDD.padding[0];
18566             r = x2 + oDD.padding[1];
18567             b = y2 + oDD.padding[2];
18568             l = x1 - oDD.padding[3];
18569
18570             return new Roo.lib.Region( t, r, b, l );
18571         },
18572
18573         /**
18574          * Checks the cursor location to see if it over the target
18575          * @method isOverTarget
18576          * @param {Roo.lib.Point} pt The point to evaluate
18577          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18578          * @return {boolean} true if the mouse is over the target
18579          * @private
18580          * @static
18581          */
18582         isOverTarget: function(pt, oTarget, intersect) {
18583             // use cache if available
18584             var loc = this.locationCache[oTarget.id];
18585             if (!loc || !this.useCache) {
18586                 loc = this.getLocation(oTarget);
18587                 this.locationCache[oTarget.id] = loc;
18588
18589             }
18590
18591             if (!loc) {
18592                 return false;
18593             }
18594
18595             oTarget.cursorIsOver = loc.contains( pt );
18596
18597             // DragDrop is using this as a sanity check for the initial mousedown
18598             // in this case we are done.  In POINT mode, if the drag obj has no
18599             // contraints, we are also done. Otherwise we need to evaluate the
18600             // location of the target as related to the actual location of the
18601             // dragged element.
18602             var dc = this.dragCurrent;
18603             if (!dc || !dc.getTargetCoord ||
18604                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18605                 return oTarget.cursorIsOver;
18606             }
18607
18608             oTarget.overlap = null;
18609
18610             // Get the current location of the drag element, this is the
18611             // location of the mouse event less the delta that represents
18612             // where the original mousedown happened on the element.  We
18613             // need to consider constraints and ticks as well.
18614             var pos = dc.getTargetCoord(pt.x, pt.y);
18615
18616             var el = dc.getDragEl();
18617             var curRegion = new Roo.lib.Region( pos.y,
18618                                                    pos.x + el.offsetWidth,
18619                                                    pos.y + el.offsetHeight,
18620                                                    pos.x );
18621
18622             var overlap = curRegion.intersect(loc);
18623
18624             if (overlap) {
18625                 oTarget.overlap = overlap;
18626                 return (intersect) ? true : oTarget.cursorIsOver;
18627             } else {
18628                 return false;
18629             }
18630         },
18631
18632         /**
18633          * unload event handler
18634          * @method _onUnload
18635          * @private
18636          * @static
18637          */
18638         _onUnload: function(e, me) {
18639             Roo.dd.DragDropMgr.unregAll();
18640         },
18641
18642         /**
18643          * Cleans up the drag and drop events and objects.
18644          * @method unregAll
18645          * @private
18646          * @static
18647          */
18648         unregAll: function() {
18649
18650             if (this.dragCurrent) {
18651                 this.stopDrag();
18652                 this.dragCurrent = null;
18653             }
18654
18655             this._execOnAll("unreg", []);
18656
18657             for (i in this.elementCache) {
18658                 delete this.elementCache[i];
18659             }
18660
18661             this.elementCache = {};
18662             this.ids = {};
18663         },
18664
18665         /**
18666          * A cache of DOM elements
18667          * @property elementCache
18668          * @private
18669          * @static
18670          */
18671         elementCache: {},
18672
18673         /**
18674          * Get the wrapper for the DOM element specified
18675          * @method getElWrapper
18676          * @param {String} id the id of the element to get
18677          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18678          * @private
18679          * @deprecated This wrapper isn't that useful
18680          * @static
18681          */
18682         getElWrapper: function(id) {
18683             var oWrapper = this.elementCache[id];
18684             if (!oWrapper || !oWrapper.el) {
18685                 oWrapper = this.elementCache[id] =
18686                     new this.ElementWrapper(Roo.getDom(id));
18687             }
18688             return oWrapper;
18689         },
18690
18691         /**
18692          * Returns the actual DOM element
18693          * @method getElement
18694          * @param {String} id the id of the elment to get
18695          * @return {Object} The element
18696          * @deprecated use Roo.getDom instead
18697          * @static
18698          */
18699         getElement: function(id) {
18700             return Roo.getDom(id);
18701         },
18702
18703         /**
18704          * Returns the style property for the DOM element (i.e.,
18705          * document.getElById(id).style)
18706          * @method getCss
18707          * @param {String} id the id of the elment to get
18708          * @return {Object} The style property of the element
18709          * @deprecated use Roo.getDom instead
18710          * @static
18711          */
18712         getCss: function(id) {
18713             var el = Roo.getDom(id);
18714             return (el) ? el.style : null;
18715         },
18716
18717         /**
18718          * Inner class for cached elements
18719          * @class DragDropMgr.ElementWrapper
18720          * @for DragDropMgr
18721          * @private
18722          * @deprecated
18723          */
18724         ElementWrapper: function(el) {
18725                 /**
18726                  * The element
18727                  * @property el
18728                  */
18729                 this.el = el || null;
18730                 /**
18731                  * The element id
18732                  * @property id
18733                  */
18734                 this.id = this.el && el.id;
18735                 /**
18736                  * A reference to the style property
18737                  * @property css
18738                  */
18739                 this.css = this.el && el.style;
18740             },
18741
18742         /**
18743          * Returns the X position of an html element
18744          * @method getPosX
18745          * @param el the element for which to get the position
18746          * @return {int} the X coordinate
18747          * @for DragDropMgr
18748          * @deprecated use Roo.lib.Dom.getX instead
18749          * @static
18750          */
18751         getPosX: function(el) {
18752             return Roo.lib.Dom.getX(el);
18753         },
18754
18755         /**
18756          * Returns the Y position of an html element
18757          * @method getPosY
18758          * @param el the element for which to get the position
18759          * @return {int} the Y coordinate
18760          * @deprecated use Roo.lib.Dom.getY instead
18761          * @static
18762          */
18763         getPosY: function(el) {
18764             return Roo.lib.Dom.getY(el);
18765         },
18766
18767         /**
18768          * Swap two nodes.  In IE, we use the native method, for others we
18769          * emulate the IE behavior
18770          * @method swapNode
18771          * @param n1 the first node to swap
18772          * @param n2 the other node to swap
18773          * @static
18774          */
18775         swapNode: function(n1, n2) {
18776             if (n1.swapNode) {
18777                 n1.swapNode(n2);
18778             } else {
18779                 var p = n2.parentNode;
18780                 var s = n2.nextSibling;
18781
18782                 if (s == n1) {
18783                     p.insertBefore(n1, n2);
18784                 } else if (n2 == n1.nextSibling) {
18785                     p.insertBefore(n2, n1);
18786                 } else {
18787                     n1.parentNode.replaceChild(n2, n1);
18788                     p.insertBefore(n1, s);
18789                 }
18790             }
18791         },
18792
18793         /**
18794          * Returns the current scroll position
18795          * @method getScroll
18796          * @private
18797          * @static
18798          */
18799         getScroll: function () {
18800             var t, l, dde=document.documentElement, db=document.body;
18801             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18802                 t = dde.scrollTop;
18803                 l = dde.scrollLeft;
18804             } else if (db) {
18805                 t = db.scrollTop;
18806                 l = db.scrollLeft;
18807             } else {
18808
18809             }
18810             return { top: t, left: l };
18811         },
18812
18813         /**
18814          * Returns the specified element style property
18815          * @method getStyle
18816          * @param {HTMLElement} el          the element
18817          * @param {string}      styleProp   the style property
18818          * @return {string} The value of the style property
18819          * @deprecated use Roo.lib.Dom.getStyle
18820          * @static
18821          */
18822         getStyle: function(el, styleProp) {
18823             return Roo.fly(el).getStyle(styleProp);
18824         },
18825
18826         /**
18827          * Gets the scrollTop
18828          * @method getScrollTop
18829          * @return {int} the document's scrollTop
18830          * @static
18831          */
18832         getScrollTop: function () { return this.getScroll().top; },
18833
18834         /**
18835          * Gets the scrollLeft
18836          * @method getScrollLeft
18837          * @return {int} the document's scrollTop
18838          * @static
18839          */
18840         getScrollLeft: function () { return this.getScroll().left; },
18841
18842         /**
18843          * Sets the x/y position of an element to the location of the
18844          * target element.
18845          * @method moveToEl
18846          * @param {HTMLElement} moveEl      The element to move
18847          * @param {HTMLElement} targetEl    The position reference element
18848          * @static
18849          */
18850         moveToEl: function (moveEl, targetEl) {
18851             var aCoord = Roo.lib.Dom.getXY(targetEl);
18852             Roo.lib.Dom.setXY(moveEl, aCoord);
18853         },
18854
18855         /**
18856          * Numeric array sort function
18857          * @method numericSort
18858          * @static
18859          */
18860         numericSort: function(a, b) { return (a - b); },
18861
18862         /**
18863          * Internal counter
18864          * @property _timeoutCount
18865          * @private
18866          * @static
18867          */
18868         _timeoutCount: 0,
18869
18870         /**
18871          * Trying to make the load order less important.  Without this we get
18872          * an error if this file is loaded before the Event Utility.
18873          * @method _addListeners
18874          * @private
18875          * @static
18876          */
18877         _addListeners: function() {
18878             var DDM = Roo.dd.DDM;
18879             if ( Roo.lib.Event && document ) {
18880                 DDM._onLoad();
18881             } else {
18882                 if (DDM._timeoutCount > 2000) {
18883                 } else {
18884                     setTimeout(DDM._addListeners, 10);
18885                     if (document && document.body) {
18886                         DDM._timeoutCount += 1;
18887                     }
18888                 }
18889             }
18890         },
18891
18892         /**
18893          * Recursively searches the immediate parent and all child nodes for
18894          * the handle element in order to determine wheter or not it was
18895          * clicked.
18896          * @method handleWasClicked
18897          * @param node the html element to inspect
18898          * @static
18899          */
18900         handleWasClicked: function(node, id) {
18901             if (this.isHandle(id, node.id)) {
18902                 return true;
18903             } else {
18904                 // check to see if this is a text node child of the one we want
18905                 var p = node.parentNode;
18906
18907                 while (p) {
18908                     if (this.isHandle(id, p.id)) {
18909                         return true;
18910                     } else {
18911                         p = p.parentNode;
18912                     }
18913                 }
18914             }
18915
18916             return false;
18917         }
18918
18919     };
18920
18921 }();
18922
18923 // shorter alias, save a few bytes
18924 Roo.dd.DDM = Roo.dd.DragDropMgr;
18925 Roo.dd.DDM._addListeners();
18926
18927 }/*
18928  * Based on:
18929  * Ext JS Library 1.1.1
18930  * Copyright(c) 2006-2007, Ext JS, LLC.
18931  *
18932  * Originally Released Under LGPL - original licence link has changed is not relivant.
18933  *
18934  * Fork - LGPL
18935  * <script type="text/javascript">
18936  */
18937
18938 /**
18939  * @class Roo.dd.DD
18940  * A DragDrop implementation where the linked element follows the
18941  * mouse cursor during a drag.
18942  * @extends Roo.dd.DragDrop
18943  * @constructor
18944  * @param {String} id the id of the linked element
18945  * @param {String} sGroup the group of related DragDrop items
18946  * @param {object} config an object containing configurable attributes
18947  *                Valid properties for DD:
18948  *                    scroll
18949  */
18950 Roo.dd.DD = function(id, sGroup, config) {
18951     if (id) {
18952         this.init(id, sGroup, config);
18953     }
18954 };
18955
18956 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18957
18958     /**
18959      * When set to true, the utility automatically tries to scroll the browser
18960      * window wehn a drag and drop element is dragged near the viewport boundary.
18961      * Defaults to true.
18962      * @property scroll
18963      * @type boolean
18964      */
18965     scroll: true,
18966
18967     /**
18968      * Sets the pointer offset to the distance between the linked element's top
18969      * left corner and the location the element was clicked
18970      * @method autoOffset
18971      * @param {int} iPageX the X coordinate of the click
18972      * @param {int} iPageY the Y coordinate of the click
18973      */
18974     autoOffset: function(iPageX, iPageY) {
18975         var x = iPageX - this.startPageX;
18976         var y = iPageY - this.startPageY;
18977         this.setDelta(x, y);
18978     },
18979
18980     /**
18981      * Sets the pointer offset.  You can call this directly to force the
18982      * offset to be in a particular location (e.g., pass in 0,0 to set it
18983      * to the center of the object)
18984      * @method setDelta
18985      * @param {int} iDeltaX the distance from the left
18986      * @param {int} iDeltaY the distance from the top
18987      */
18988     setDelta: function(iDeltaX, iDeltaY) {
18989         this.deltaX = iDeltaX;
18990         this.deltaY = iDeltaY;
18991     },
18992
18993     /**
18994      * Sets the drag element to the location of the mousedown or click event,
18995      * maintaining the cursor location relative to the location on the element
18996      * that was clicked.  Override this if you want to place the element in a
18997      * location other than where the cursor is.
18998      * @method setDragElPos
18999      * @param {int} iPageX the X coordinate of the mousedown or drag event
19000      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19001      */
19002     setDragElPos: function(iPageX, iPageY) {
19003         // the first time we do this, we are going to check to make sure
19004         // the element has css positioning
19005
19006         var el = this.getDragEl();
19007         this.alignElWithMouse(el, iPageX, iPageY);
19008     },
19009
19010     /**
19011      * Sets the element to the location of the mousedown or click event,
19012      * maintaining the cursor location relative to the location on the element
19013      * that was clicked.  Override this if you want to place the element in a
19014      * location other than where the cursor is.
19015      * @method alignElWithMouse
19016      * @param {HTMLElement} el the element to move
19017      * @param {int} iPageX the X coordinate of the mousedown or drag event
19018      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19019      */
19020     alignElWithMouse: function(el, iPageX, iPageY) {
19021         var oCoord = this.getTargetCoord(iPageX, iPageY);
19022         var fly = el.dom ? el : Roo.fly(el);
19023         if (!this.deltaSetXY) {
19024             var aCoord = [oCoord.x, oCoord.y];
19025             fly.setXY(aCoord);
19026             var newLeft = fly.getLeft(true);
19027             var newTop  = fly.getTop(true);
19028             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19029         } else {
19030             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19031         }
19032
19033         this.cachePosition(oCoord.x, oCoord.y);
19034         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19035         return oCoord;
19036     },
19037
19038     /**
19039      * Saves the most recent position so that we can reset the constraints and
19040      * tick marks on-demand.  We need to know this so that we can calculate the
19041      * number of pixels the element is offset from its original position.
19042      * @method cachePosition
19043      * @param iPageX the current x position (optional, this just makes it so we
19044      * don't have to look it up again)
19045      * @param iPageY the current y position (optional, this just makes it so we
19046      * don't have to look it up again)
19047      */
19048     cachePosition: function(iPageX, iPageY) {
19049         if (iPageX) {
19050             this.lastPageX = iPageX;
19051             this.lastPageY = iPageY;
19052         } else {
19053             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19054             this.lastPageX = aCoord[0];
19055             this.lastPageY = aCoord[1];
19056         }
19057     },
19058
19059     /**
19060      * Auto-scroll the window if the dragged object has been moved beyond the
19061      * visible window boundary.
19062      * @method autoScroll
19063      * @param {int} x the drag element's x position
19064      * @param {int} y the drag element's y position
19065      * @param {int} h the height of the drag element
19066      * @param {int} w the width of the drag element
19067      * @private
19068      */
19069     autoScroll: function(x, y, h, w) {
19070
19071         if (this.scroll) {
19072             // The client height
19073             var clientH = Roo.lib.Dom.getViewWidth();
19074
19075             // The client width
19076             var clientW = Roo.lib.Dom.getViewHeight();
19077
19078             // The amt scrolled down
19079             var st = this.DDM.getScrollTop();
19080
19081             // The amt scrolled right
19082             var sl = this.DDM.getScrollLeft();
19083
19084             // Location of the bottom of the element
19085             var bot = h + y;
19086
19087             // Location of the right of the element
19088             var right = w + x;
19089
19090             // The distance from the cursor to the bottom of the visible area,
19091             // adjusted so that we don't scroll if the cursor is beyond the
19092             // element drag constraints
19093             var toBot = (clientH + st - y - this.deltaY);
19094
19095             // The distance from the cursor to the right of the visible area
19096             var toRight = (clientW + sl - x - this.deltaX);
19097
19098
19099             // How close to the edge the cursor must be before we scroll
19100             // var thresh = (document.all) ? 100 : 40;
19101             var thresh = 40;
19102
19103             // How many pixels to scroll per autoscroll op.  This helps to reduce
19104             // clunky scrolling. IE is more sensitive about this ... it needs this
19105             // value to be higher.
19106             var scrAmt = (document.all) ? 80 : 30;
19107
19108             // Scroll down if we are near the bottom of the visible page and the
19109             // obj extends below the crease
19110             if ( bot > clientH && toBot < thresh ) {
19111                 window.scrollTo(sl, st + scrAmt);
19112             }
19113
19114             // Scroll up if the window is scrolled down and the top of the object
19115             // goes above the top border
19116             if ( y < st && st > 0 && y - st < thresh ) {
19117                 window.scrollTo(sl, st - scrAmt);
19118             }
19119
19120             // Scroll right if the obj is beyond the right border and the cursor is
19121             // near the border.
19122             if ( right > clientW && toRight < thresh ) {
19123                 window.scrollTo(sl + scrAmt, st);
19124             }
19125
19126             // Scroll left if the window has been scrolled to the right and the obj
19127             // extends past the left border
19128             if ( x < sl && sl > 0 && x - sl < thresh ) {
19129                 window.scrollTo(sl - scrAmt, st);
19130             }
19131         }
19132     },
19133
19134     /**
19135      * Finds the location the element should be placed if we want to move
19136      * it to where the mouse location less the click offset would place us.
19137      * @method getTargetCoord
19138      * @param {int} iPageX the X coordinate of the click
19139      * @param {int} iPageY the Y coordinate of the click
19140      * @return an object that contains the coordinates (Object.x and Object.y)
19141      * @private
19142      */
19143     getTargetCoord: function(iPageX, iPageY) {
19144
19145
19146         var x = iPageX - this.deltaX;
19147         var y = iPageY - this.deltaY;
19148
19149         if (this.constrainX) {
19150             if (x < this.minX) { x = this.minX; }
19151             if (x > this.maxX) { x = this.maxX; }
19152         }
19153
19154         if (this.constrainY) {
19155             if (y < this.minY) { y = this.minY; }
19156             if (y > this.maxY) { y = this.maxY; }
19157         }
19158
19159         x = this.getTick(x, this.xTicks);
19160         y = this.getTick(y, this.yTicks);
19161
19162
19163         return {x:x, y:y};
19164     },
19165
19166     /*
19167      * Sets up config options specific to this class. Overrides
19168      * Roo.dd.DragDrop, but all versions of this method through the
19169      * inheritance chain are called
19170      */
19171     applyConfig: function() {
19172         Roo.dd.DD.superclass.applyConfig.call(this);
19173         this.scroll = (this.config.scroll !== false);
19174     },
19175
19176     /*
19177      * Event that fires prior to the onMouseDown event.  Overrides
19178      * Roo.dd.DragDrop.
19179      */
19180     b4MouseDown: function(e) {
19181         // this.resetConstraints();
19182         this.autoOffset(e.getPageX(),
19183                             e.getPageY());
19184     },
19185
19186     /*
19187      * Event that fires prior to the onDrag event.  Overrides
19188      * Roo.dd.DragDrop.
19189      */
19190     b4Drag: function(e) {
19191         this.setDragElPos(e.getPageX(),
19192                             e.getPageY());
19193     },
19194
19195     toString: function() {
19196         return ("DD " + this.id);
19197     }
19198
19199     //////////////////////////////////////////////////////////////////////////
19200     // Debugging ygDragDrop events that can be overridden
19201     //////////////////////////////////////////////////////////////////////////
19202     /*
19203     startDrag: function(x, y) {
19204     },
19205
19206     onDrag: function(e) {
19207     },
19208
19209     onDragEnter: function(e, id) {
19210     },
19211
19212     onDragOver: function(e, id) {
19213     },
19214
19215     onDragOut: function(e, id) {
19216     },
19217
19218     onDragDrop: function(e, id) {
19219     },
19220
19221     endDrag: function(e) {
19222     }
19223
19224     */
19225
19226 });/*
19227  * Based on:
19228  * Ext JS Library 1.1.1
19229  * Copyright(c) 2006-2007, Ext JS, LLC.
19230  *
19231  * Originally Released Under LGPL - original licence link has changed is not relivant.
19232  *
19233  * Fork - LGPL
19234  * <script type="text/javascript">
19235  */
19236
19237 /**
19238  * @class Roo.dd.DDProxy
19239  * A DragDrop implementation that inserts an empty, bordered div into
19240  * the document that follows the cursor during drag operations.  At the time of
19241  * the click, the frame div is resized to the dimensions of the linked html
19242  * element, and moved to the exact location of the linked element.
19243  *
19244  * References to the "frame" element refer to the single proxy element that
19245  * was created to be dragged in place of all DDProxy elements on the
19246  * page.
19247  *
19248  * @extends Roo.dd.DD
19249  * @constructor
19250  * @param {String} id the id of the linked html element
19251  * @param {String} sGroup the group of related DragDrop objects
19252  * @param {object} config an object containing configurable attributes
19253  *                Valid properties for DDProxy in addition to those in DragDrop:
19254  *                   resizeFrame, centerFrame, dragElId
19255  */
19256 Roo.dd.DDProxy = function(id, sGroup, config) {
19257     if (id) {
19258         this.init(id, sGroup, config);
19259         this.initFrame();
19260     }
19261 };
19262
19263 /**
19264  * The default drag frame div id
19265  * @property Roo.dd.DDProxy.dragElId
19266  * @type String
19267  * @static
19268  */
19269 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19270
19271 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19272
19273     /**
19274      * By default we resize the drag frame to be the same size as the element
19275      * we want to drag (this is to get the frame effect).  We can turn it off
19276      * if we want a different behavior.
19277      * @property resizeFrame
19278      * @type boolean
19279      */
19280     resizeFrame: true,
19281
19282     /**
19283      * By default the frame is positioned exactly where the drag element is, so
19284      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19285      * you do not have constraints on the obj is to have the drag frame centered
19286      * around the cursor.  Set centerFrame to true for this effect.
19287      * @property centerFrame
19288      * @type boolean
19289      */
19290     centerFrame: false,
19291
19292     /**
19293      * Creates the proxy element if it does not yet exist
19294      * @method createFrame
19295      */
19296     createFrame: function() {
19297         var self = this;
19298         var body = document.body;
19299
19300         if (!body || !body.firstChild) {
19301             setTimeout( function() { self.createFrame(); }, 50 );
19302             return;
19303         }
19304
19305         var div = this.getDragEl();
19306
19307         if (!div) {
19308             div    = document.createElement("div");
19309             div.id = this.dragElId;
19310             var s  = div.style;
19311
19312             s.position   = "absolute";
19313             s.visibility = "hidden";
19314             s.cursor     = "move";
19315             s.border     = "2px solid #aaa";
19316             s.zIndex     = 999;
19317
19318             // appendChild can blow up IE if invoked prior to the window load event
19319             // while rendering a table.  It is possible there are other scenarios
19320             // that would cause this to happen as well.
19321             body.insertBefore(div, body.firstChild);
19322         }
19323     },
19324
19325     /**
19326      * Initialization for the drag frame element.  Must be called in the
19327      * constructor of all subclasses
19328      * @method initFrame
19329      */
19330     initFrame: function() {
19331         this.createFrame();
19332     },
19333
19334     applyConfig: function() {
19335         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19336
19337         this.resizeFrame = (this.config.resizeFrame !== false);
19338         this.centerFrame = (this.config.centerFrame);
19339         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19340     },
19341
19342     /**
19343      * Resizes the drag frame to the dimensions of the clicked object, positions
19344      * it over the object, and finally displays it
19345      * @method showFrame
19346      * @param {int} iPageX X click position
19347      * @param {int} iPageY Y click position
19348      * @private
19349      */
19350     showFrame: function(iPageX, iPageY) {
19351         var el = this.getEl();
19352         var dragEl = this.getDragEl();
19353         var s = dragEl.style;
19354
19355         this._resizeProxy();
19356
19357         if (this.centerFrame) {
19358             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19359                            Math.round(parseInt(s.height, 10)/2) );
19360         }
19361
19362         this.setDragElPos(iPageX, iPageY);
19363
19364         Roo.fly(dragEl).show();
19365     },
19366
19367     /**
19368      * The proxy is automatically resized to the dimensions of the linked
19369      * element when a drag is initiated, unless resizeFrame is set to false
19370      * @method _resizeProxy
19371      * @private
19372      */
19373     _resizeProxy: function() {
19374         if (this.resizeFrame) {
19375             var el = this.getEl();
19376             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19377         }
19378     },
19379
19380     // overrides Roo.dd.DragDrop
19381     b4MouseDown: function(e) {
19382         var x = e.getPageX();
19383         var y = e.getPageY();
19384         this.autoOffset(x, y);
19385         this.setDragElPos(x, y);
19386     },
19387
19388     // overrides Roo.dd.DragDrop
19389     b4StartDrag: function(x, y) {
19390         // show the drag frame
19391         this.showFrame(x, y);
19392     },
19393
19394     // overrides Roo.dd.DragDrop
19395     b4EndDrag: function(e) {
19396         Roo.fly(this.getDragEl()).hide();
19397     },
19398
19399     // overrides Roo.dd.DragDrop
19400     // By default we try to move the element to the last location of the frame.
19401     // This is so that the default behavior mirrors that of Roo.dd.DD.
19402     endDrag: function(e) {
19403
19404         var lel = this.getEl();
19405         var del = this.getDragEl();
19406
19407         // Show the drag frame briefly so we can get its position
19408         del.style.visibility = "";
19409
19410         this.beforeMove();
19411         // Hide the linked element before the move to get around a Safari
19412         // rendering bug.
19413         lel.style.visibility = "hidden";
19414         Roo.dd.DDM.moveToEl(lel, del);
19415         del.style.visibility = "hidden";
19416         lel.style.visibility = "";
19417
19418         this.afterDrag();
19419     },
19420
19421     beforeMove : function(){
19422
19423     },
19424
19425     afterDrag : function(){
19426
19427     },
19428
19429     toString: function() {
19430         return ("DDProxy " + this.id);
19431     }
19432
19433 });
19434 /*
19435  * Based on:
19436  * Ext JS Library 1.1.1
19437  * Copyright(c) 2006-2007, Ext JS, LLC.
19438  *
19439  * Originally Released Under LGPL - original licence link has changed is not relivant.
19440  *
19441  * Fork - LGPL
19442  * <script type="text/javascript">
19443  */
19444
19445  /**
19446  * @class Roo.dd.DDTarget
19447  * A DragDrop implementation that does not move, but can be a drop
19448  * target.  You would get the same result by simply omitting implementation
19449  * for the event callbacks, but this way we reduce the processing cost of the
19450  * event listener and the callbacks.
19451  * @extends Roo.dd.DragDrop
19452  * @constructor
19453  * @param {String} id the id of the element that is a drop target
19454  * @param {String} sGroup the group of related DragDrop objects
19455  * @param {object} config an object containing configurable attributes
19456  *                 Valid properties for DDTarget in addition to those in
19457  *                 DragDrop:
19458  *                    none
19459  */
19460 Roo.dd.DDTarget = function(id, sGroup, config) {
19461     if (id) {
19462         this.initTarget(id, sGroup, config);
19463     }
19464     if (config.listeners || config.events) { 
19465        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19466             listeners : config.listeners || {}, 
19467             events : config.events || {} 
19468         });    
19469     }
19470 };
19471
19472 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19473 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19474     toString: function() {
19475         return ("DDTarget " + this.id);
19476     }
19477 });
19478 /*
19479  * Based on:
19480  * Ext JS Library 1.1.1
19481  * Copyright(c) 2006-2007, Ext JS, LLC.
19482  *
19483  * Originally Released Under LGPL - original licence link has changed is not relivant.
19484  *
19485  * Fork - LGPL
19486  * <script type="text/javascript">
19487  */
19488  
19489
19490 /**
19491  * @class Roo.dd.ScrollManager
19492  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19493  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19494  * @singleton
19495  */
19496 Roo.dd.ScrollManager = function(){
19497     var ddm = Roo.dd.DragDropMgr;
19498     var els = {};
19499     var dragEl = null;
19500     var proc = {};
19501     
19502     
19503     
19504     var onStop = function(e){
19505         dragEl = null;
19506         clearProc();
19507     };
19508     
19509     var triggerRefresh = function(){
19510         if(ddm.dragCurrent){
19511              ddm.refreshCache(ddm.dragCurrent.groups);
19512         }
19513     };
19514     
19515     var doScroll = function(){
19516         if(ddm.dragCurrent){
19517             var dds = Roo.dd.ScrollManager;
19518             if(!dds.animate){
19519                 if(proc.el.scroll(proc.dir, dds.increment)){
19520                     triggerRefresh();
19521                 }
19522             }else{
19523                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19524             }
19525         }
19526     };
19527     
19528     var clearProc = function(){
19529         if(proc.id){
19530             clearInterval(proc.id);
19531         }
19532         proc.id = 0;
19533         proc.el = null;
19534         proc.dir = "";
19535     };
19536     
19537     var startProc = function(el, dir){
19538          Roo.log('scroll startproc');
19539         clearProc();
19540         proc.el = el;
19541         proc.dir = dir;
19542         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19543     };
19544     
19545     var onFire = function(e, isDrop){
19546        
19547         if(isDrop || !ddm.dragCurrent){ return; }
19548         var dds = Roo.dd.ScrollManager;
19549         if(!dragEl || dragEl != ddm.dragCurrent){
19550             dragEl = ddm.dragCurrent;
19551             // refresh regions on drag start
19552             dds.refreshCache();
19553         }
19554         
19555         var xy = Roo.lib.Event.getXY(e);
19556         var pt = new Roo.lib.Point(xy[0], xy[1]);
19557         for(var id in els){
19558             var el = els[id], r = el._region;
19559             if(r && r.contains(pt) && el.isScrollable()){
19560                 if(r.bottom - pt.y <= dds.thresh){
19561                     if(proc.el != el){
19562                         startProc(el, "down");
19563                     }
19564                     return;
19565                 }else if(r.right - pt.x <= dds.thresh){
19566                     if(proc.el != el){
19567                         startProc(el, "left");
19568                     }
19569                     return;
19570                 }else if(pt.y - r.top <= dds.thresh){
19571                     if(proc.el != el){
19572                         startProc(el, "up");
19573                     }
19574                     return;
19575                 }else if(pt.x - r.left <= dds.thresh){
19576                     if(proc.el != el){
19577                         startProc(el, "right");
19578                     }
19579                     return;
19580                 }
19581             }
19582         }
19583         clearProc();
19584     };
19585     
19586     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19587     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19588     
19589     return {
19590         /**
19591          * Registers new overflow element(s) to auto scroll
19592          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19593          */
19594         register : function(el){
19595             if(el instanceof Array){
19596                 for(var i = 0, len = el.length; i < len; i++) {
19597                         this.register(el[i]);
19598                 }
19599             }else{
19600                 el = Roo.get(el);
19601                 els[el.id] = el;
19602             }
19603             Roo.dd.ScrollManager.els = els;
19604         },
19605         
19606         /**
19607          * Unregisters overflow element(s) so they are no longer scrolled
19608          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19609          */
19610         unregister : function(el){
19611             if(el instanceof Array){
19612                 for(var i = 0, len = el.length; i < len; i++) {
19613                         this.unregister(el[i]);
19614                 }
19615             }else{
19616                 el = Roo.get(el);
19617                 delete els[el.id];
19618             }
19619         },
19620         
19621         /**
19622          * The number of pixels from the edge of a container the pointer needs to be to 
19623          * trigger scrolling (defaults to 25)
19624          * @type Number
19625          */
19626         thresh : 25,
19627         
19628         /**
19629          * The number of pixels to scroll in each scroll increment (defaults to 50)
19630          * @type Number
19631          */
19632         increment : 100,
19633         
19634         /**
19635          * The frequency of scrolls in milliseconds (defaults to 500)
19636          * @type Number
19637          */
19638         frequency : 500,
19639         
19640         /**
19641          * True to animate the scroll (defaults to true)
19642          * @type Boolean
19643          */
19644         animate: true,
19645         
19646         /**
19647          * The animation duration in seconds - 
19648          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19649          * @type Number
19650          */
19651         animDuration: .4,
19652         
19653         /**
19654          * Manually trigger a cache refresh.
19655          */
19656         refreshCache : function(){
19657             for(var id in els){
19658                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19659                     els[id]._region = els[id].getRegion();
19660                 }
19661             }
19662         }
19663     };
19664 }();/*
19665  * Based on:
19666  * Ext JS Library 1.1.1
19667  * Copyright(c) 2006-2007, Ext JS, LLC.
19668  *
19669  * Originally Released Under LGPL - original licence link has changed is not relivant.
19670  *
19671  * Fork - LGPL
19672  * <script type="text/javascript">
19673  */
19674  
19675
19676 /**
19677  * @class Roo.dd.Registry
19678  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19679  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19680  * @singleton
19681  */
19682 Roo.dd.Registry = function(){
19683     var elements = {}; 
19684     var handles = {}; 
19685     var autoIdSeed = 0;
19686
19687     var getId = function(el, autogen){
19688         if(typeof el == "string"){
19689             return el;
19690         }
19691         var id = el.id;
19692         if(!id && autogen !== false){
19693             id = "roodd-" + (++autoIdSeed);
19694             el.id = id;
19695         }
19696         return id;
19697     };
19698     
19699     return {
19700     /**
19701      * Register a drag drop element
19702      * @param {String|HTMLElement} element The id or DOM node to register
19703      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19704      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19705      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19706      * populated in the data object (if applicable):
19707      * <pre>
19708 Value      Description<br />
19709 ---------  ------------------------------------------<br />
19710 handles    Array of DOM nodes that trigger dragging<br />
19711            for the element being registered<br />
19712 isHandle   True if the element passed in triggers<br />
19713            dragging itself, else false
19714 </pre>
19715      */
19716         register : function(el, data){
19717             data = data || {};
19718             if(typeof el == "string"){
19719                 el = document.getElementById(el);
19720             }
19721             data.ddel = el;
19722             elements[getId(el)] = data;
19723             if(data.isHandle !== false){
19724                 handles[data.ddel.id] = data;
19725             }
19726             if(data.handles){
19727                 var hs = data.handles;
19728                 for(var i = 0, len = hs.length; i < len; i++){
19729                         handles[getId(hs[i])] = data;
19730                 }
19731             }
19732         },
19733
19734     /**
19735      * Unregister a drag drop element
19736      * @param {String|HTMLElement}  element The id or DOM node to unregister
19737      */
19738         unregister : function(el){
19739             var id = getId(el, false);
19740             var data = elements[id];
19741             if(data){
19742                 delete elements[id];
19743                 if(data.handles){
19744                     var hs = data.handles;
19745                     for(var i = 0, len = hs.length; i < len; i++){
19746                         delete handles[getId(hs[i], false)];
19747                     }
19748                 }
19749             }
19750         },
19751
19752     /**
19753      * Returns the handle registered for a DOM Node by id
19754      * @param {String|HTMLElement} id The DOM node or id to look up
19755      * @return {Object} handle The custom handle data
19756      */
19757         getHandle : function(id){
19758             if(typeof id != "string"){ // must be element?
19759                 id = id.id;
19760             }
19761             return handles[id];
19762         },
19763
19764     /**
19765      * Returns the handle that is registered for the DOM node that is the target of the event
19766      * @param {Event} e The event
19767      * @return {Object} handle The custom handle data
19768      */
19769         getHandleFromEvent : function(e){
19770             var t = Roo.lib.Event.getTarget(e);
19771             return t ? handles[t.id] : null;
19772         },
19773
19774     /**
19775      * Returns a custom data object that is registered for a DOM node by id
19776      * @param {String|HTMLElement} id The DOM node or id to look up
19777      * @return {Object} data The custom data
19778      */
19779         getTarget : function(id){
19780             if(typeof id != "string"){ // must be element?
19781                 id = id.id;
19782             }
19783             return elements[id];
19784         },
19785
19786     /**
19787      * Returns a custom data object that is registered for the DOM node that is the target of the event
19788      * @param {Event} e The event
19789      * @return {Object} data The custom data
19790      */
19791         getTargetFromEvent : function(e){
19792             var t = Roo.lib.Event.getTarget(e);
19793             return t ? elements[t.id] || handles[t.id] : null;
19794         }
19795     };
19796 }();/*
19797  * Based on:
19798  * Ext JS Library 1.1.1
19799  * Copyright(c) 2006-2007, Ext JS, LLC.
19800  *
19801  * Originally Released Under LGPL - original licence link has changed is not relivant.
19802  *
19803  * Fork - LGPL
19804  * <script type="text/javascript">
19805  */
19806  
19807
19808 /**
19809  * @class Roo.dd.StatusProxy
19810  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19811  * default drag proxy used by all Roo.dd components.
19812  * @constructor
19813  * @param {Object} config
19814  */
19815 Roo.dd.StatusProxy = function(config){
19816     Roo.apply(this, config);
19817     this.id = this.id || Roo.id();
19818     this.el = new Roo.Layer({
19819         dh: {
19820             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19821                 {tag: "div", cls: "x-dd-drop-icon"},
19822                 {tag: "div", cls: "x-dd-drag-ghost"}
19823             ]
19824         }, 
19825         shadow: !config || config.shadow !== false
19826     });
19827     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19828     this.dropStatus = this.dropNotAllowed;
19829 };
19830
19831 Roo.dd.StatusProxy.prototype = {
19832     /**
19833      * @cfg {String} dropAllowed
19834      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19835      */
19836     dropAllowed : "x-dd-drop-ok",
19837     /**
19838      * @cfg {String} dropNotAllowed
19839      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19840      */
19841     dropNotAllowed : "x-dd-drop-nodrop",
19842
19843     /**
19844      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19845      * over the current target element.
19846      * @param {String} cssClass The css class for the new drop status indicator image
19847      */
19848     setStatus : function(cssClass){
19849         cssClass = cssClass || this.dropNotAllowed;
19850         if(this.dropStatus != cssClass){
19851             this.el.replaceClass(this.dropStatus, cssClass);
19852             this.dropStatus = cssClass;
19853         }
19854     },
19855
19856     /**
19857      * Resets the status indicator to the default dropNotAllowed value
19858      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19859      */
19860     reset : function(clearGhost){
19861         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19862         this.dropStatus = this.dropNotAllowed;
19863         if(clearGhost){
19864             this.ghost.update("");
19865         }
19866     },
19867
19868     /**
19869      * Updates the contents of the ghost element
19870      * @param {String} html The html that will replace the current innerHTML of the ghost element
19871      */
19872     update : function(html){
19873         if(typeof html == "string"){
19874             this.ghost.update(html);
19875         }else{
19876             this.ghost.update("");
19877             html.style.margin = "0";
19878             this.ghost.dom.appendChild(html);
19879         }
19880         // ensure float = none set?? cant remember why though.
19881         var el = this.ghost.dom.firstChild;
19882                 if(el){
19883                         Roo.fly(el).setStyle('float', 'none');
19884                 }
19885     },
19886     
19887     /**
19888      * Returns the underlying proxy {@link Roo.Layer}
19889      * @return {Roo.Layer} el
19890     */
19891     getEl : function(){
19892         return this.el;
19893     },
19894
19895     /**
19896      * Returns the ghost element
19897      * @return {Roo.Element} el
19898      */
19899     getGhost : function(){
19900         return this.ghost;
19901     },
19902
19903     /**
19904      * Hides the proxy
19905      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19906      */
19907     hide : function(clear){
19908         this.el.hide();
19909         if(clear){
19910             this.reset(true);
19911         }
19912     },
19913
19914     /**
19915      * Stops the repair animation if it's currently running
19916      */
19917     stop : function(){
19918         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19919             this.anim.stop();
19920         }
19921     },
19922
19923     /**
19924      * Displays this proxy
19925      */
19926     show : function(){
19927         this.el.show();
19928     },
19929
19930     /**
19931      * Force the Layer to sync its shadow and shim positions to the element
19932      */
19933     sync : function(){
19934         this.el.sync();
19935     },
19936
19937     /**
19938      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19939      * invalid drop operation by the item being dragged.
19940      * @param {Array} xy The XY position of the element ([x, y])
19941      * @param {Function} callback The function to call after the repair is complete
19942      * @param {Object} scope The scope in which to execute the callback
19943      */
19944     repair : function(xy, callback, scope){
19945         this.callback = callback;
19946         this.scope = scope;
19947         if(xy && this.animRepair !== false){
19948             this.el.addClass("x-dd-drag-repair");
19949             this.el.hideUnders(true);
19950             this.anim = this.el.shift({
19951                 duration: this.repairDuration || .5,
19952                 easing: 'easeOut',
19953                 xy: xy,
19954                 stopFx: true,
19955                 callback: this.afterRepair,
19956                 scope: this
19957             });
19958         }else{
19959             this.afterRepair();
19960         }
19961     },
19962
19963     // private
19964     afterRepair : function(){
19965         this.hide(true);
19966         if(typeof this.callback == "function"){
19967             this.callback.call(this.scope || this);
19968         }
19969         this.callback = null;
19970         this.scope = null;
19971     }
19972 };/*
19973  * Based on:
19974  * Ext JS Library 1.1.1
19975  * Copyright(c) 2006-2007, Ext JS, LLC.
19976  *
19977  * Originally Released Under LGPL - original licence link has changed is not relivant.
19978  *
19979  * Fork - LGPL
19980  * <script type="text/javascript">
19981  */
19982
19983 /**
19984  * @class Roo.dd.DragSource
19985  * @extends Roo.dd.DDProxy
19986  * A simple class that provides the basic implementation needed to make any element draggable.
19987  * @constructor
19988  * @param {String/HTMLElement/Element} el The container element
19989  * @param {Object} config
19990  */
19991 Roo.dd.DragSource = function(el, config){
19992     this.el = Roo.get(el);
19993     this.dragData = {};
19994     
19995     Roo.apply(this, config);
19996     
19997     if(!this.proxy){
19998         this.proxy = new Roo.dd.StatusProxy();
19999     }
20000
20001     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20002           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20003     
20004     this.dragging = false;
20005 };
20006
20007 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20008     /**
20009      * @cfg {String} dropAllowed
20010      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20011      */
20012     dropAllowed : "x-dd-drop-ok",
20013     /**
20014      * @cfg {String} dropNotAllowed
20015      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20016      */
20017     dropNotAllowed : "x-dd-drop-nodrop",
20018
20019     /**
20020      * Returns the data object associated with this drag source
20021      * @return {Object} data An object containing arbitrary data
20022      */
20023     getDragData : function(e){
20024         return this.dragData;
20025     },
20026
20027     // private
20028     onDragEnter : function(e, id){
20029         var target = Roo.dd.DragDropMgr.getDDById(id);
20030         this.cachedTarget = target;
20031         if(this.beforeDragEnter(target, e, id) !== false){
20032             if(target.isNotifyTarget){
20033                 var status = target.notifyEnter(this, e, this.dragData);
20034                 this.proxy.setStatus(status);
20035             }else{
20036                 this.proxy.setStatus(this.dropAllowed);
20037             }
20038             
20039             if(this.afterDragEnter){
20040                 /**
20041                  * An empty function by default, but provided so that you can perform a custom action
20042                  * when the dragged item enters the drop target by providing an implementation.
20043                  * @param {Roo.dd.DragDrop} target The drop target
20044                  * @param {Event} e The event object
20045                  * @param {String} id The id of the dragged element
20046                  * @method afterDragEnter
20047                  */
20048                 this.afterDragEnter(target, e, id);
20049             }
20050         }
20051     },
20052
20053     /**
20054      * An empty function by default, but provided so that you can perform a custom action
20055      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20056      * @param {Roo.dd.DragDrop} target The drop target
20057      * @param {Event} e The event object
20058      * @param {String} id The id of the dragged element
20059      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20060      */
20061     beforeDragEnter : function(target, e, id){
20062         return true;
20063     },
20064
20065     // private
20066     alignElWithMouse: function() {
20067         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20068         this.proxy.sync();
20069     },
20070
20071     // private
20072     onDragOver : function(e, id){
20073         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20074         if(this.beforeDragOver(target, e, id) !== false){
20075             if(target.isNotifyTarget){
20076                 var status = target.notifyOver(this, e, this.dragData);
20077                 this.proxy.setStatus(status);
20078             }
20079
20080             if(this.afterDragOver){
20081                 /**
20082                  * An empty function by default, but provided so that you can perform a custom action
20083                  * while the dragged item is over the drop target by providing an implementation.
20084                  * @param {Roo.dd.DragDrop} target The drop target
20085                  * @param {Event} e The event object
20086                  * @param {String} id The id of the dragged element
20087                  * @method afterDragOver
20088                  */
20089                 this.afterDragOver(target, e, id);
20090             }
20091         }
20092     },
20093
20094     /**
20095      * An empty function by default, but provided so that you can perform a custom action
20096      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20097      * @param {Roo.dd.DragDrop} target The drop target
20098      * @param {Event} e The event object
20099      * @param {String} id The id of the dragged element
20100      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20101      */
20102     beforeDragOver : function(target, e, id){
20103         return true;
20104     },
20105
20106     // private
20107     onDragOut : function(e, id){
20108         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20109         if(this.beforeDragOut(target, e, id) !== false){
20110             if(target.isNotifyTarget){
20111                 target.notifyOut(this, e, this.dragData);
20112             }
20113             this.proxy.reset();
20114             if(this.afterDragOut){
20115                 /**
20116                  * An empty function by default, but provided so that you can perform a custom action
20117                  * after the dragged item is dragged out of the target without dropping.
20118                  * @param {Roo.dd.DragDrop} target The drop target
20119                  * @param {Event} e The event object
20120                  * @param {String} id The id of the dragged element
20121                  * @method afterDragOut
20122                  */
20123                 this.afterDragOut(target, e, id);
20124             }
20125         }
20126         this.cachedTarget = null;
20127     },
20128
20129     /**
20130      * An empty function by default, but provided so that you can perform a custom action before the dragged
20131      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20132      * @param {Roo.dd.DragDrop} target The drop target
20133      * @param {Event} e The event object
20134      * @param {String} id The id of the dragged element
20135      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20136      */
20137     beforeDragOut : function(target, e, id){
20138         return true;
20139     },
20140     
20141     // private
20142     onDragDrop : function(e, id){
20143         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20144         if(this.beforeDragDrop(target, e, id) !== false){
20145             if(target.isNotifyTarget){
20146                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20147                     this.onValidDrop(target, e, id);
20148                 }else{
20149                     this.onInvalidDrop(target, e, id);
20150                 }
20151             }else{
20152                 this.onValidDrop(target, e, id);
20153             }
20154             
20155             if(this.afterDragDrop){
20156                 /**
20157                  * An empty function by default, but provided so that you can perform a custom action
20158                  * after a valid drag drop has occurred by providing an implementation.
20159                  * @param {Roo.dd.DragDrop} target The drop target
20160                  * @param {Event} e The event object
20161                  * @param {String} id The id of the dropped element
20162                  * @method afterDragDrop
20163                  */
20164                 this.afterDragDrop(target, e, id);
20165             }
20166         }
20167         delete this.cachedTarget;
20168     },
20169
20170     /**
20171      * An empty function by default, but provided so that you can perform a custom action before the dragged
20172      * item is dropped onto the target and optionally cancel the onDragDrop.
20173      * @param {Roo.dd.DragDrop} target The drop target
20174      * @param {Event} e The event object
20175      * @param {String} id The id of the dragged element
20176      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20177      */
20178     beforeDragDrop : function(target, e, id){
20179         return true;
20180     },
20181
20182     // private
20183     onValidDrop : function(target, e, id){
20184         this.hideProxy();
20185         if(this.afterValidDrop){
20186             /**
20187              * An empty function by default, but provided so that you can perform a custom action
20188              * after a valid drop has occurred by providing an implementation.
20189              * @param {Object} target The target DD 
20190              * @param {Event} e The event object
20191              * @param {String} id The id of the dropped element
20192              * @method afterInvalidDrop
20193              */
20194             this.afterValidDrop(target, e, id);
20195         }
20196     },
20197
20198     // private
20199     getRepairXY : function(e, data){
20200         return this.el.getXY();  
20201     },
20202
20203     // private
20204     onInvalidDrop : function(target, e, id){
20205         this.beforeInvalidDrop(target, e, id);
20206         if(this.cachedTarget){
20207             if(this.cachedTarget.isNotifyTarget){
20208                 this.cachedTarget.notifyOut(this, e, this.dragData);
20209             }
20210             this.cacheTarget = null;
20211         }
20212         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20213
20214         if(this.afterInvalidDrop){
20215             /**
20216              * An empty function by default, but provided so that you can perform a custom action
20217              * after an invalid drop has occurred by providing an implementation.
20218              * @param {Event} e The event object
20219              * @param {String} id The id of the dropped element
20220              * @method afterInvalidDrop
20221              */
20222             this.afterInvalidDrop(e, id);
20223         }
20224     },
20225
20226     // private
20227     afterRepair : function(){
20228         if(Roo.enableFx){
20229             this.el.highlight(this.hlColor || "c3daf9");
20230         }
20231         this.dragging = false;
20232     },
20233
20234     /**
20235      * An empty function by default, but provided so that you can perform a custom action after an invalid
20236      * drop has occurred.
20237      * @param {Roo.dd.DragDrop} target The drop target
20238      * @param {Event} e The event object
20239      * @param {String} id The id of the dragged element
20240      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20241      */
20242     beforeInvalidDrop : function(target, e, id){
20243         return true;
20244     },
20245
20246     // private
20247     handleMouseDown : function(e){
20248         if(this.dragging) {
20249             return;
20250         }
20251         var data = this.getDragData(e);
20252         if(data && this.onBeforeDrag(data, e) !== false){
20253             this.dragData = data;
20254             this.proxy.stop();
20255             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20256         } 
20257     },
20258
20259     /**
20260      * An empty function by default, but provided so that you can perform a custom action before the initial
20261      * drag event begins and optionally cancel it.
20262      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20263      * @param {Event} e The event object
20264      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20265      */
20266     onBeforeDrag : function(data, e){
20267         return true;
20268     },
20269
20270     /**
20271      * An empty function by default, but provided so that you can perform a custom action once the initial
20272      * drag event has begun.  The drag cannot be canceled from this function.
20273      * @param {Number} x The x position of the click on the dragged object
20274      * @param {Number} y The y position of the click on the dragged object
20275      */
20276     onStartDrag : Roo.emptyFn,
20277
20278     // private - YUI override
20279     startDrag : function(x, y){
20280         this.proxy.reset();
20281         this.dragging = true;
20282         this.proxy.update("");
20283         this.onInitDrag(x, y);
20284         this.proxy.show();
20285     },
20286
20287     // private
20288     onInitDrag : function(x, y){
20289         var clone = this.el.dom.cloneNode(true);
20290         clone.id = Roo.id(); // prevent duplicate ids
20291         this.proxy.update(clone);
20292         this.onStartDrag(x, y);
20293         return true;
20294     },
20295
20296     /**
20297      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20298      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20299      */
20300     getProxy : function(){
20301         return this.proxy;  
20302     },
20303
20304     /**
20305      * Hides the drag source's {@link Roo.dd.StatusProxy}
20306      */
20307     hideProxy : function(){
20308         this.proxy.hide();  
20309         this.proxy.reset(true);
20310         this.dragging = false;
20311     },
20312
20313     // private
20314     triggerCacheRefresh : function(){
20315         Roo.dd.DDM.refreshCache(this.groups);
20316     },
20317
20318     // private - override to prevent hiding
20319     b4EndDrag: function(e) {
20320     },
20321
20322     // private - override to prevent moving
20323     endDrag : function(e){
20324         this.onEndDrag(this.dragData, e);
20325     },
20326
20327     // private
20328     onEndDrag : function(data, e){
20329     },
20330     
20331     // private - pin to cursor
20332     autoOffset : function(x, y) {
20333         this.setDelta(-12, -20);
20334     }    
20335 });/*
20336  * Based on:
20337  * Ext JS Library 1.1.1
20338  * Copyright(c) 2006-2007, Ext JS, LLC.
20339  *
20340  * Originally Released Under LGPL - original licence link has changed is not relivant.
20341  *
20342  * Fork - LGPL
20343  * <script type="text/javascript">
20344  */
20345
20346
20347 /**
20348  * @class Roo.dd.DropTarget
20349  * @extends Roo.dd.DDTarget
20350  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20351  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20352  * @constructor
20353  * @param {String/HTMLElement/Element} el The container element
20354  * @param {Object} config
20355  */
20356 Roo.dd.DropTarget = function(el, config){
20357     this.el = Roo.get(el);
20358     
20359     var listeners = false; ;
20360     if (config && config.listeners) {
20361         listeners= config.listeners;
20362         delete config.listeners;
20363     }
20364     Roo.apply(this, config);
20365     
20366     if(this.containerScroll){
20367         Roo.dd.ScrollManager.register(this.el);
20368     }
20369     this.addEvents( {
20370          /**
20371          * @scope Roo.dd.DropTarget
20372          */
20373          
20374          /**
20375          * @event enter
20376          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20377          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20378          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20379          * 
20380          * IMPORTANT : it should set this.overClass and this.dropAllowed
20381          * 
20382          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20383          * @param {Event} e The event
20384          * @param {Object} data An object containing arbitrary data supplied by the drag source
20385          */
20386         "enter" : true,
20387         
20388          /**
20389          * @event over
20390          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20391          * This method will be called on every mouse movement while the drag source is over the drop target.
20392          * This default implementation simply returns the dropAllowed config value.
20393          * 
20394          * IMPORTANT : it should set this.dropAllowed
20395          * 
20396          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20397          * @param {Event} e The event
20398          * @param {Object} data An object containing arbitrary data supplied by the drag source
20399          
20400          */
20401         "over" : true,
20402         /**
20403          * @event out
20404          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20405          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20406          * overClass (if any) from the drop element.
20407          * 
20408          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20409          * @param {Event} e The event
20410          * @param {Object} data An object containing arbitrary data supplied by the drag source
20411          */
20412          "out" : true,
20413          
20414         /**
20415          * @event drop
20416          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20417          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20418          * implementation that does something to process the drop event and returns true so that the drag source's
20419          * repair action does not run.
20420          * 
20421          * IMPORTANT : it should set this.success
20422          * 
20423          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20424          * @param {Event} e The event
20425          * @param {Object} data An object containing arbitrary data supplied by the drag source
20426         */
20427          "drop" : true
20428     });
20429             
20430      
20431     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20432         this.el.dom, 
20433         this.ddGroup || this.group,
20434         {
20435             isTarget: true,
20436             listeners : listeners || {} 
20437            
20438         
20439         }
20440     );
20441
20442 };
20443
20444 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20445     /**
20446      * @cfg {String} overClass
20447      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20448      */
20449      /**
20450      * @cfg {String} ddGroup
20451      * The drag drop group to handle drop events for
20452      */
20453      
20454     /**
20455      * @cfg {String} dropAllowed
20456      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20457      */
20458     dropAllowed : "x-dd-drop-ok",
20459     /**
20460      * @cfg {String} dropNotAllowed
20461      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20462      */
20463     dropNotAllowed : "x-dd-drop-nodrop",
20464     /**
20465      * @cfg {boolean} success
20466      * set this after drop listener.. 
20467      */
20468     success : false,
20469     /**
20470      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20471      * if the drop point is valid for over/enter..
20472      */
20473     valid : false,
20474     // private
20475     isTarget : true,
20476
20477     // private
20478     isNotifyTarget : true,
20479     
20480     /**
20481      * @hide
20482      */
20483     notifyEnter : function(dd, e, data)
20484     {
20485         this.valid = true;
20486         this.fireEvent('enter', dd, e, data);
20487         if(this.overClass){
20488             this.el.addClass(this.overClass);
20489         }
20490         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20491             this.valid ? this.dropAllowed : this.dropNotAllowed
20492         );
20493     },
20494
20495     /**
20496      * @hide
20497      */
20498     notifyOver : function(dd, e, data)
20499     {
20500         this.valid = true;
20501         this.fireEvent('over', dd, e, data);
20502         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20503             this.valid ? this.dropAllowed : this.dropNotAllowed
20504         );
20505     },
20506
20507     /**
20508      * @hide
20509      */
20510     notifyOut : function(dd, e, data)
20511     {
20512         this.fireEvent('out', dd, e, data);
20513         if(this.overClass){
20514             this.el.removeClass(this.overClass);
20515         }
20516     },
20517
20518     /**
20519      * @hide
20520      */
20521     notifyDrop : function(dd, e, data)
20522     {
20523         this.success = false;
20524         this.fireEvent('drop', dd, e, data);
20525         return this.success;
20526     }
20527 });/*
20528  * Based on:
20529  * Ext JS Library 1.1.1
20530  * Copyright(c) 2006-2007, Ext JS, LLC.
20531  *
20532  * Originally Released Under LGPL - original licence link has changed is not relivant.
20533  *
20534  * Fork - LGPL
20535  * <script type="text/javascript">
20536  */
20537
20538
20539 /**
20540  * @class Roo.dd.DragZone
20541  * @extends Roo.dd.DragSource
20542  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20543  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20544  * @constructor
20545  * @param {String/HTMLElement/Element} el The container element
20546  * @param {Object} config
20547  */
20548 Roo.dd.DragZone = function(el, config){
20549     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20550     if(this.containerScroll){
20551         Roo.dd.ScrollManager.register(this.el);
20552     }
20553 };
20554
20555 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20556     /**
20557      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20558      * for auto scrolling during drag operations.
20559      */
20560     /**
20561      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20562      * method after a failed drop (defaults to "c3daf9" - light blue)
20563      */
20564
20565     /**
20566      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20567      * for a valid target to drag based on the mouse down. Override this method
20568      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20569      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20570      * @param {EventObject} e The mouse down event
20571      * @return {Object} The dragData
20572      */
20573     getDragData : function(e){
20574         return Roo.dd.Registry.getHandleFromEvent(e);
20575     },
20576     
20577     /**
20578      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20579      * this.dragData.ddel
20580      * @param {Number} x The x position of the click on the dragged object
20581      * @param {Number} y The y position of the click on the dragged object
20582      * @return {Boolean} true to continue the drag, false to cancel
20583      */
20584     onInitDrag : function(x, y){
20585         this.proxy.update(this.dragData.ddel.cloneNode(true));
20586         this.onStartDrag(x, y);
20587         return true;
20588     },
20589     
20590     /**
20591      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20592      */
20593     afterRepair : function(){
20594         if(Roo.enableFx){
20595             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20596         }
20597         this.dragging = false;
20598     },
20599
20600     /**
20601      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20602      * the XY of this.dragData.ddel
20603      * @param {EventObject} e The mouse up event
20604      * @return {Array} The xy location (e.g. [100, 200])
20605      */
20606     getRepairXY : function(e){
20607         return Roo.Element.fly(this.dragData.ddel).getXY();  
20608     }
20609 });/*
20610  * Based on:
20611  * Ext JS Library 1.1.1
20612  * Copyright(c) 2006-2007, Ext JS, LLC.
20613  *
20614  * Originally Released Under LGPL - original licence link has changed is not relivant.
20615  *
20616  * Fork - LGPL
20617  * <script type="text/javascript">
20618  */
20619 /**
20620  * @class Roo.dd.DropZone
20621  * @extends Roo.dd.DropTarget
20622  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20623  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20624  * @constructor
20625  * @param {String/HTMLElement/Element} el The container element
20626  * @param {Object} config
20627  */
20628 Roo.dd.DropZone = function(el, config){
20629     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20630 };
20631
20632 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20633     /**
20634      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20635      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20636      * provide your own custom lookup.
20637      * @param {Event} e The event
20638      * @return {Object} data The custom data
20639      */
20640     getTargetFromEvent : function(e){
20641         return Roo.dd.Registry.getTargetFromEvent(e);
20642     },
20643
20644     /**
20645      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20646      * that it has registered.  This method has no default implementation and should be overridden to provide
20647      * node-specific processing if necessary.
20648      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20649      * {@link #getTargetFromEvent} for this node)
20650      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20651      * @param {Event} e The event
20652      * @param {Object} data An object containing arbitrary data supplied by the drag source
20653      */
20654     onNodeEnter : function(n, dd, e, data){
20655         
20656     },
20657
20658     /**
20659      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20660      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20661      * overridden to provide the proper feedback.
20662      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20663      * {@link #getTargetFromEvent} for this node)
20664      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20665      * @param {Event} e The event
20666      * @param {Object} data An object containing arbitrary data supplied by the drag source
20667      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20668      * underlying {@link Roo.dd.StatusProxy} can be updated
20669      */
20670     onNodeOver : function(n, dd, e, data){
20671         return this.dropAllowed;
20672     },
20673
20674     /**
20675      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20676      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20677      * node-specific processing if necessary.
20678      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20679      * {@link #getTargetFromEvent} for this node)
20680      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20681      * @param {Event} e The event
20682      * @param {Object} data An object containing arbitrary data supplied by the drag source
20683      */
20684     onNodeOut : function(n, dd, e, data){
20685         
20686     },
20687
20688     /**
20689      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20690      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20691      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20692      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20693      * {@link #getTargetFromEvent} for this node)
20694      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20695      * @param {Event} e The event
20696      * @param {Object} data An object containing arbitrary data supplied by the drag source
20697      * @return {Boolean} True if the drop was valid, else false
20698      */
20699     onNodeDrop : function(n, dd, e, data){
20700         return false;
20701     },
20702
20703     /**
20704      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20705      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20706      * it should be overridden to provide the proper feedback if necessary.
20707      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20708      * @param {Event} e The event
20709      * @param {Object} data An object containing arbitrary data supplied by the drag source
20710      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20711      * underlying {@link Roo.dd.StatusProxy} can be updated
20712      */
20713     onContainerOver : function(dd, e, data){
20714         return this.dropNotAllowed;
20715     },
20716
20717     /**
20718      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20719      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20720      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20721      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20723      * @param {Event} e The event
20724      * @param {Object} data An object containing arbitrary data supplied by the drag source
20725      * @return {Boolean} True if the drop was valid, else false
20726      */
20727     onContainerDrop : function(dd, e, data){
20728         return false;
20729     },
20730
20731     /**
20732      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20733      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20734      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20735      * you should override this method and provide a custom implementation.
20736      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20737      * @param {Event} e The event
20738      * @param {Object} data An object containing arbitrary data supplied by the drag source
20739      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20740      * underlying {@link Roo.dd.StatusProxy} can be updated
20741      */
20742     notifyEnter : function(dd, e, data){
20743         return this.dropNotAllowed;
20744     },
20745
20746     /**
20747      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20748      * This method will be called on every mouse movement while the drag source is over the drop zone.
20749      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20750      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20751      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20752      * registered node, it will call {@link #onContainerOver}.
20753      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20754      * @param {Event} e The event
20755      * @param {Object} data An object containing arbitrary data supplied by the drag source
20756      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20757      * underlying {@link Roo.dd.StatusProxy} can be updated
20758      */
20759     notifyOver : function(dd, e, data){
20760         var n = this.getTargetFromEvent(e);
20761         if(!n){ // not over valid drop target
20762             if(this.lastOverNode){
20763                 this.onNodeOut(this.lastOverNode, dd, e, data);
20764                 this.lastOverNode = null;
20765             }
20766             return this.onContainerOver(dd, e, data);
20767         }
20768         if(this.lastOverNode != n){
20769             if(this.lastOverNode){
20770                 this.onNodeOut(this.lastOverNode, dd, e, data);
20771             }
20772             this.onNodeEnter(n, dd, e, data);
20773             this.lastOverNode = n;
20774         }
20775         return this.onNodeOver(n, dd, e, data);
20776     },
20777
20778     /**
20779      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20780      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20781      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20782      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20783      * @param {Event} e The event
20784      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20785      */
20786     notifyOut : function(dd, e, data){
20787         if(this.lastOverNode){
20788             this.onNodeOut(this.lastOverNode, dd, e, data);
20789             this.lastOverNode = null;
20790         }
20791     },
20792
20793     /**
20794      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20795      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20796      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20797      * otherwise it will call {@link #onContainerDrop}.
20798      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20799      * @param {Event} e The event
20800      * @param {Object} data An object containing arbitrary data supplied by the drag source
20801      * @return {Boolean} True if the drop was valid, else false
20802      */
20803     notifyDrop : function(dd, e, data){
20804         if(this.lastOverNode){
20805             this.onNodeOut(this.lastOverNode, dd, e, data);
20806             this.lastOverNode = null;
20807         }
20808         var n = this.getTargetFromEvent(e);
20809         return n ?
20810             this.onNodeDrop(n, dd, e, data) :
20811             this.onContainerDrop(dd, e, data);
20812     },
20813
20814     // private
20815     triggerCacheRefresh : function(){
20816         Roo.dd.DDM.refreshCache(this.groups);
20817     }  
20818 });/*
20819  * Based on:
20820  * Ext JS Library 1.1.1
20821  * Copyright(c) 2006-2007, Ext JS, LLC.
20822  *
20823  * Originally Released Under LGPL - original licence link has changed is not relivant.
20824  *
20825  * Fork - LGPL
20826  * <script type="text/javascript">
20827  */
20828
20829
20830 /**
20831  * @class Roo.data.SortTypes
20832  * @singleton
20833  * Defines the default sorting (casting?) comparison functions used when sorting data.
20834  */
20835 Roo.data.SortTypes = {
20836     /**
20837      * Default sort that does nothing
20838      * @param {Mixed} s The value being converted
20839      * @return {Mixed} The comparison value
20840      */
20841     none : function(s){
20842         return s;
20843     },
20844     
20845     /**
20846      * The regular expression used to strip tags
20847      * @type {RegExp}
20848      * @property
20849      */
20850     stripTagsRE : /<\/?[^>]+>/gi,
20851     
20852     /**
20853      * Strips all HTML tags to sort on text only
20854      * @param {Mixed} s The value being converted
20855      * @return {String} The comparison value
20856      */
20857     asText : function(s){
20858         return String(s).replace(this.stripTagsRE, "");
20859     },
20860     
20861     /**
20862      * Strips all HTML tags to sort on text only - Case insensitive
20863      * @param {Mixed} s The value being converted
20864      * @return {String} The comparison value
20865      */
20866     asUCText : function(s){
20867         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20868     },
20869     
20870     /**
20871      * Case insensitive string
20872      * @param {Mixed} s The value being converted
20873      * @return {String} The comparison value
20874      */
20875     asUCString : function(s) {
20876         return String(s).toUpperCase();
20877     },
20878     
20879     /**
20880      * Date sorting
20881      * @param {Mixed} s The value being converted
20882      * @return {Number} The comparison value
20883      */
20884     asDate : function(s) {
20885         if(!s){
20886             return 0;
20887         }
20888         if(s instanceof Date){
20889             return s.getTime();
20890         }
20891         return Date.parse(String(s));
20892     },
20893     
20894     /**
20895      * Float sorting
20896      * @param {Mixed} s The value being converted
20897      * @return {Float} The comparison value
20898      */
20899     asFloat : function(s) {
20900         var val = parseFloat(String(s).replace(/,/g, ""));
20901         if(isNaN(val)) val = 0;
20902         return val;
20903     },
20904     
20905     /**
20906      * Integer sorting
20907      * @param {Mixed} s The value being converted
20908      * @return {Number} The comparison value
20909      */
20910     asInt : function(s) {
20911         var val = parseInt(String(s).replace(/,/g, ""));
20912         if(isNaN(val)) val = 0;
20913         return val;
20914     }
20915 };/*
20916  * Based on:
20917  * Ext JS Library 1.1.1
20918  * Copyright(c) 2006-2007, Ext JS, LLC.
20919  *
20920  * Originally Released Under LGPL - original licence link has changed is not relivant.
20921  *
20922  * Fork - LGPL
20923  * <script type="text/javascript">
20924  */
20925
20926 /**
20927 * @class Roo.data.Record
20928  * Instances of this class encapsulate both record <em>definition</em> information, and record
20929  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20930  * to access Records cached in an {@link Roo.data.Store} object.<br>
20931  * <p>
20932  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20933  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20934  * objects.<br>
20935  * <p>
20936  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20937  * @constructor
20938  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20939  * {@link #create}. The parameters are the same.
20940  * @param {Array} data An associative Array of data values keyed by the field name.
20941  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20942  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20943  * not specified an integer id is generated.
20944  */
20945 Roo.data.Record = function(data, id){
20946     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20947     this.data = data;
20948 };
20949
20950 /**
20951  * Generate a constructor for a specific record layout.
20952  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20953  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20954  * Each field definition object may contain the following properties: <ul>
20955  * <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,
20956  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20957  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20958  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20959  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20960  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20961  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20962  * this may be omitted.</p></li>
20963  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20964  * <ul><li>auto (Default, implies no conversion)</li>
20965  * <li>string</li>
20966  * <li>int</li>
20967  * <li>float</li>
20968  * <li>boolean</li>
20969  * <li>date</li></ul></p></li>
20970  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20971  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20972  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20973  * by the Reader into an object that will be stored in the Record. It is passed the
20974  * following parameters:<ul>
20975  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20976  * </ul></p></li>
20977  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20978  * </ul>
20979  * <br>usage:<br><pre><code>
20980 var TopicRecord = Roo.data.Record.create(
20981     {name: 'title', mapping: 'topic_title'},
20982     {name: 'author', mapping: 'username'},
20983     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20984     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20985     {name: 'lastPoster', mapping: 'user2'},
20986     {name: 'excerpt', mapping: 'post_text'}
20987 );
20988
20989 var myNewRecord = new TopicRecord({
20990     title: 'Do my job please',
20991     author: 'noobie',
20992     totalPosts: 1,
20993     lastPost: new Date(),
20994     lastPoster: 'Animal',
20995     excerpt: 'No way dude!'
20996 });
20997 myStore.add(myNewRecord);
20998 </code></pre>
20999  * @method create
21000  * @static
21001  */
21002 Roo.data.Record.create = function(o){
21003     var f = function(){
21004         f.superclass.constructor.apply(this, arguments);
21005     };
21006     Roo.extend(f, Roo.data.Record);
21007     var p = f.prototype;
21008     p.fields = new Roo.util.MixedCollection(false, function(field){
21009         return field.name;
21010     });
21011     for(var i = 0, len = o.length; i < len; i++){
21012         p.fields.add(new Roo.data.Field(o[i]));
21013     }
21014     f.getField = function(name){
21015         return p.fields.get(name);  
21016     };
21017     return f;
21018 };
21019
21020 Roo.data.Record.AUTO_ID = 1000;
21021 Roo.data.Record.EDIT = 'edit';
21022 Roo.data.Record.REJECT = 'reject';
21023 Roo.data.Record.COMMIT = 'commit';
21024
21025 Roo.data.Record.prototype = {
21026     /**
21027      * Readonly flag - true if this record has been modified.
21028      * @type Boolean
21029      */
21030     dirty : false,
21031     editing : false,
21032     error: null,
21033     modified: null,
21034
21035     // private
21036     join : function(store){
21037         this.store = store;
21038     },
21039
21040     /**
21041      * Set the named field to the specified value.
21042      * @param {String} name The name of the field to set.
21043      * @param {Object} value The value to set the field to.
21044      */
21045     set : function(name, value){
21046         if(this.data[name] == value){
21047             return;
21048         }
21049         this.dirty = true;
21050         if(!this.modified){
21051             this.modified = {};
21052         }
21053         if(typeof this.modified[name] == 'undefined'){
21054             this.modified[name] = this.data[name];
21055         }
21056         this.data[name] = value;
21057         if(!this.editing && this.store){
21058             this.store.afterEdit(this);
21059         }       
21060     },
21061
21062     /**
21063      * Get the value of the named field.
21064      * @param {String} name The name of the field to get the value of.
21065      * @return {Object} The value of the field.
21066      */
21067     get : function(name){
21068         return this.data[name]; 
21069     },
21070
21071     // private
21072     beginEdit : function(){
21073         this.editing = true;
21074         this.modified = {}; 
21075     },
21076
21077     // private
21078     cancelEdit : function(){
21079         this.editing = false;
21080         delete this.modified;
21081     },
21082
21083     // private
21084     endEdit : function(){
21085         this.editing = false;
21086         if(this.dirty && this.store){
21087             this.store.afterEdit(this);
21088         }
21089     },
21090
21091     /**
21092      * Usually called by the {@link Roo.data.Store} which owns the Record.
21093      * Rejects all changes made to the Record since either creation, or the last commit operation.
21094      * Modified fields are reverted to their original values.
21095      * <p>
21096      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21097      * of reject operations.
21098      */
21099     reject : function(){
21100         var m = this.modified;
21101         for(var n in m){
21102             if(typeof m[n] != "function"){
21103                 this.data[n] = m[n];
21104             }
21105         }
21106         this.dirty = false;
21107         delete this.modified;
21108         this.editing = false;
21109         if(this.store){
21110             this.store.afterReject(this);
21111         }
21112     },
21113
21114     /**
21115      * Usually called by the {@link Roo.data.Store} which owns the Record.
21116      * Commits all changes made to the Record since either creation, or the last commit operation.
21117      * <p>
21118      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21119      * of commit operations.
21120      */
21121     commit : function(){
21122         this.dirty = false;
21123         delete this.modified;
21124         this.editing = false;
21125         if(this.store){
21126             this.store.afterCommit(this);
21127         }
21128     },
21129
21130     // private
21131     hasError : function(){
21132         return this.error != null;
21133     },
21134
21135     // private
21136     clearError : function(){
21137         this.error = null;
21138     },
21139
21140     /**
21141      * Creates a copy of this record.
21142      * @param {String} id (optional) A new record id if you don't want to use this record's id
21143      * @return {Record}
21144      */
21145     copy : function(newId) {
21146         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21147     }
21148 };/*
21149  * Based on:
21150  * Ext JS Library 1.1.1
21151  * Copyright(c) 2006-2007, Ext JS, LLC.
21152  *
21153  * Originally Released Under LGPL - original licence link has changed is not relivant.
21154  *
21155  * Fork - LGPL
21156  * <script type="text/javascript">
21157  */
21158
21159
21160
21161 /**
21162  * @class Roo.data.Store
21163  * @extends Roo.util.Observable
21164  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21165  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21166  * <p>
21167  * 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
21168  * has no knowledge of the format of the data returned by the Proxy.<br>
21169  * <p>
21170  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21171  * instances from the data object. These records are cached and made available through accessor functions.
21172  * @constructor
21173  * Creates a new Store.
21174  * @param {Object} config A config object containing the objects needed for the Store to access data,
21175  * and read the data into Records.
21176  */
21177 Roo.data.Store = function(config){
21178     this.data = new Roo.util.MixedCollection(false);
21179     this.data.getKey = function(o){
21180         return o.id;
21181     };
21182     this.baseParams = {};
21183     // private
21184     this.paramNames = {
21185         "start" : "start",
21186         "limit" : "limit",
21187         "sort" : "sort",
21188         "dir" : "dir",
21189         "multisort" : "_multisort"
21190     };
21191
21192     if(config && config.data){
21193         this.inlineData = config.data;
21194         delete config.data;
21195     }
21196
21197     Roo.apply(this, config);
21198     
21199     if(this.reader){ // reader passed
21200         this.reader = Roo.factory(this.reader, Roo.data);
21201         this.reader.xmodule = this.xmodule || false;
21202         if(!this.recordType){
21203             this.recordType = this.reader.recordType;
21204         }
21205         if(this.reader.onMetaChange){
21206             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21207         }
21208     }
21209
21210     if(this.recordType){
21211         this.fields = this.recordType.prototype.fields;
21212     }
21213     this.modified = [];
21214
21215     this.addEvents({
21216         /**
21217          * @event datachanged
21218          * Fires when the data cache has changed, and a widget which is using this Store
21219          * as a Record cache should refresh its view.
21220          * @param {Store} this
21221          */
21222         datachanged : true,
21223         /**
21224          * @event metachange
21225          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21226          * @param {Store} this
21227          * @param {Object} meta The JSON metadata
21228          */
21229         metachange : true,
21230         /**
21231          * @event add
21232          * Fires when Records have been added to the Store
21233          * @param {Store} this
21234          * @param {Roo.data.Record[]} records The array of Records added
21235          * @param {Number} index The index at which the record(s) were added
21236          */
21237         add : true,
21238         /**
21239          * @event remove
21240          * Fires when a Record has been removed from the Store
21241          * @param {Store} this
21242          * @param {Roo.data.Record} record The Record that was removed
21243          * @param {Number} index The index at which the record was removed
21244          */
21245         remove : true,
21246         /**
21247          * @event update
21248          * Fires when a Record has been updated
21249          * @param {Store} this
21250          * @param {Roo.data.Record} record The Record that was updated
21251          * @param {String} operation The update operation being performed.  Value may be one of:
21252          * <pre><code>
21253  Roo.data.Record.EDIT
21254  Roo.data.Record.REJECT
21255  Roo.data.Record.COMMIT
21256          * </code></pre>
21257          */
21258         update : true,
21259         /**
21260          * @event clear
21261          * Fires when the data cache has been cleared.
21262          * @param {Store} this
21263          */
21264         clear : true,
21265         /**
21266          * @event beforeload
21267          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21268          * the load action will be canceled.
21269          * @param {Store} this
21270          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21271          */
21272         beforeload : true,
21273         /**
21274          * @event beforeloadadd
21275          * Fires after a new set of Records has been loaded.
21276          * @param {Store} this
21277          * @param {Roo.data.Record[]} records The Records that were loaded
21278          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21279          */
21280         beforeloadadd : true,
21281         /**
21282          * @event load
21283          * Fires after a new set of Records has been loaded, before they are added to the store.
21284          * @param {Store} this
21285          * @param {Roo.data.Record[]} records The Records that were loaded
21286          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21287          * @params {Object} return from reader
21288          */
21289         load : true,
21290         /**
21291          * @event loadexception
21292          * Fires if an exception occurs in the Proxy during loading.
21293          * Called with the signature of the Proxy's "loadexception" event.
21294          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21295          * 
21296          * @param {Proxy} 
21297          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21298          * @param {Object} load options 
21299          * @param {Object} jsonData from your request (normally this contains the Exception)
21300          */
21301         loadexception : true
21302     });
21303     
21304     if(this.proxy){
21305         this.proxy = Roo.factory(this.proxy, Roo.data);
21306         this.proxy.xmodule = this.xmodule || false;
21307         this.relayEvents(this.proxy,  ["loadexception"]);
21308     }
21309     this.sortToggle = {};
21310     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21311
21312     Roo.data.Store.superclass.constructor.call(this);
21313
21314     if(this.inlineData){
21315         this.loadData(this.inlineData);
21316         delete this.inlineData;
21317     }
21318 };
21319
21320 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21321      /**
21322     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21323     * without a remote query - used by combo/forms at present.
21324     */
21325     
21326     /**
21327     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21328     */
21329     /**
21330     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21331     */
21332     /**
21333     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21334     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21335     */
21336     /**
21337     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21338     * on any HTTP request
21339     */
21340     /**
21341     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21342     */
21343     /**
21344     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21345     */
21346     multiSort: false,
21347     /**
21348     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21349     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21350     */
21351     remoteSort : false,
21352
21353     /**
21354     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21355      * loaded or when a record is removed. (defaults to false).
21356     */
21357     pruneModifiedRecords : false,
21358
21359     // private
21360     lastOptions : null,
21361
21362     /**
21363      * Add Records to the Store and fires the add event.
21364      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21365      */
21366     add : function(records){
21367         records = [].concat(records);
21368         for(var i = 0, len = records.length; i < len; i++){
21369             records[i].join(this);
21370         }
21371         var index = this.data.length;
21372         this.data.addAll(records);
21373         this.fireEvent("add", this, records, index);
21374     },
21375
21376     /**
21377      * Remove a Record from the Store and fires the remove event.
21378      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21379      */
21380     remove : function(record){
21381         var index = this.data.indexOf(record);
21382         this.data.removeAt(index);
21383         if(this.pruneModifiedRecords){
21384             this.modified.remove(record);
21385         }
21386         this.fireEvent("remove", this, record, index);
21387     },
21388
21389     /**
21390      * Remove all Records from the Store and fires the clear event.
21391      */
21392     removeAll : function(){
21393         this.data.clear();
21394         if(this.pruneModifiedRecords){
21395             this.modified = [];
21396         }
21397         this.fireEvent("clear", this);
21398     },
21399
21400     /**
21401      * Inserts Records to the Store at the given index and fires the add event.
21402      * @param {Number} index The start index at which to insert the passed Records.
21403      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21404      */
21405     insert : function(index, records){
21406         records = [].concat(records);
21407         for(var i = 0, len = records.length; i < len; i++){
21408             this.data.insert(index, records[i]);
21409             records[i].join(this);
21410         }
21411         this.fireEvent("add", this, records, index);
21412     },
21413
21414     /**
21415      * Get the index within the cache of the passed Record.
21416      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21417      * @return {Number} The index of the passed Record. Returns -1 if not found.
21418      */
21419     indexOf : function(record){
21420         return this.data.indexOf(record);
21421     },
21422
21423     /**
21424      * Get the index within the cache of the Record with the passed id.
21425      * @param {String} id The id of the Record to find.
21426      * @return {Number} The index of the Record. Returns -1 if not found.
21427      */
21428     indexOfId : function(id){
21429         return this.data.indexOfKey(id);
21430     },
21431
21432     /**
21433      * Get the Record with the specified id.
21434      * @param {String} id The id of the Record to find.
21435      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21436      */
21437     getById : function(id){
21438         return this.data.key(id);
21439     },
21440
21441     /**
21442      * Get the Record at the specified index.
21443      * @param {Number} index The index of the Record to find.
21444      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21445      */
21446     getAt : function(index){
21447         return this.data.itemAt(index);
21448     },
21449
21450     /**
21451      * Returns a range of Records between specified indices.
21452      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21453      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21454      * @return {Roo.data.Record[]} An array of Records
21455      */
21456     getRange : function(start, end){
21457         return this.data.getRange(start, end);
21458     },
21459
21460     // private
21461     storeOptions : function(o){
21462         o = Roo.apply({}, o);
21463         delete o.callback;
21464         delete o.scope;
21465         this.lastOptions = o;
21466     },
21467
21468     /**
21469      * Loads the Record cache from the configured Proxy using the configured Reader.
21470      * <p>
21471      * If using remote paging, then the first load call must specify the <em>start</em>
21472      * and <em>limit</em> properties in the options.params property to establish the initial
21473      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21474      * <p>
21475      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21476      * and this call will return before the new data has been loaded. Perform any post-processing
21477      * in a callback function, or in a "load" event handler.</strong>
21478      * <p>
21479      * @param {Object} options An object containing properties which control loading options:<ul>
21480      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21481      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21482      * passed the following arguments:<ul>
21483      * <li>r : Roo.data.Record[]</li>
21484      * <li>options: Options object from the load call</li>
21485      * <li>success: Boolean success indicator</li></ul></li>
21486      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21487      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21488      * </ul>
21489      */
21490     load : function(options){
21491         options = options || {};
21492         if(this.fireEvent("beforeload", this, options) !== false){
21493             this.storeOptions(options);
21494             var p = Roo.apply(options.params || {}, this.baseParams);
21495             // if meta was not loaded from remote source.. try requesting it.
21496             if (!this.reader.metaFromRemote) {
21497                 p._requestMeta = 1;
21498             }
21499             if(this.sortInfo && this.remoteSort){
21500                 var pn = this.paramNames;
21501                 p[pn["sort"]] = this.sortInfo.field;
21502                 p[pn["dir"]] = this.sortInfo.direction;
21503             }
21504             if (this.multiSort) {
21505                 var pn = this.paramNames;
21506                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21507             }
21508             
21509             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21510         }
21511     },
21512
21513     /**
21514      * Reloads the Record cache from the configured Proxy using the configured Reader and
21515      * the options from the last load operation performed.
21516      * @param {Object} options (optional) An object containing properties which may override the options
21517      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21518      * the most recently used options are reused).
21519      */
21520     reload : function(options){
21521         this.load(Roo.applyIf(options||{}, this.lastOptions));
21522     },
21523
21524     // private
21525     // Called as a callback by the Reader during a load operation.
21526     loadRecords : function(o, options, success){
21527         if(!o || success === false){
21528             if(success !== false){
21529                 this.fireEvent("load", this, [], options, o);
21530             }
21531             if(options.callback){
21532                 options.callback.call(options.scope || this, [], options, false);
21533             }
21534             return;
21535         }
21536         // if data returned failure - throw an exception.
21537         if (o.success === false) {
21538             // show a message if no listener is registered.
21539             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21540                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21541             }
21542             // loadmask wil be hooked into this..
21543             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21544             return;
21545         }
21546         var r = o.records, t = o.totalRecords || r.length;
21547         
21548         this.fireEvent("beforeloadadd", this, r, options, o);
21549         
21550         if(!options || options.add !== true){
21551             if(this.pruneModifiedRecords){
21552                 this.modified = [];
21553             }
21554             for(var i = 0, len = r.length; i < len; i++){
21555                 r[i].join(this);
21556             }
21557             if(this.snapshot){
21558                 this.data = this.snapshot;
21559                 delete this.snapshot;
21560             }
21561             this.data.clear();
21562             this.data.addAll(r);
21563             this.totalLength = t;
21564             this.applySort();
21565             this.fireEvent("datachanged", this);
21566         }else{
21567             this.totalLength = Math.max(t, this.data.length+r.length);
21568             this.add(r);
21569         }
21570         this.fireEvent("load", this, r, options, o);
21571         if(options.callback){
21572             options.callback.call(options.scope || this, r, options, true);
21573         }
21574     },
21575
21576
21577     /**
21578      * Loads data from a passed data block. A Reader which understands the format of the data
21579      * must have been configured in the constructor.
21580      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21581      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21582      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21583      */
21584     loadData : function(o, append){
21585         var r = this.reader.readRecords(o);
21586         this.loadRecords(r, {add: append}, true);
21587     },
21588
21589     /**
21590      * Gets the number of cached records.
21591      * <p>
21592      * <em>If using paging, this may not be the total size of the dataset. If the data object
21593      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21594      * the data set size</em>
21595      */
21596     getCount : function(){
21597         return this.data.length || 0;
21598     },
21599
21600     /**
21601      * Gets the total number of records in the dataset as returned by the server.
21602      * <p>
21603      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21604      * the dataset size</em>
21605      */
21606     getTotalCount : function(){
21607         return this.totalLength || 0;
21608     },
21609
21610     /**
21611      * Returns the sort state of the Store as an object with two properties:
21612      * <pre><code>
21613  field {String} The name of the field by which the Records are sorted
21614  direction {String} The sort order, "ASC" or "DESC"
21615      * </code></pre>
21616      */
21617     getSortState : function(){
21618         return this.sortInfo;
21619     },
21620
21621     // private
21622     applySort : function(){
21623         if(this.sortInfo && !this.remoteSort){
21624             var s = this.sortInfo, f = s.field;
21625             var st = this.fields.get(f).sortType;
21626             var fn = function(r1, r2){
21627                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21628                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21629             };
21630             this.data.sort(s.direction, fn);
21631             if(this.snapshot && this.snapshot != this.data){
21632                 this.snapshot.sort(s.direction, fn);
21633             }
21634         }
21635     },
21636
21637     /**
21638      * Sets the default sort column and order to be used by the next load operation.
21639      * @param {String} fieldName The name of the field to sort by.
21640      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21641      */
21642     setDefaultSort : function(field, dir){
21643         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21644     },
21645
21646     /**
21647      * Sort the Records.
21648      * If remote sorting is used, the sort is performed on the server, and the cache is
21649      * reloaded. If local sorting is used, the cache is sorted internally.
21650      * @param {String} fieldName The name of the field to sort by.
21651      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21652      */
21653     sort : function(fieldName, dir){
21654         var f = this.fields.get(fieldName);
21655         if(!dir){
21656             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21657             
21658             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21659                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21660             }else{
21661                 dir = f.sortDir;
21662             }
21663         }
21664         this.sortToggle[f.name] = dir;
21665         this.sortInfo = {field: f.name, direction: dir};
21666         if(!this.remoteSort){
21667             this.applySort();
21668             this.fireEvent("datachanged", this);
21669         }else{
21670             this.load(this.lastOptions);
21671         }
21672     },
21673
21674     /**
21675      * Calls the specified function for each of the Records in the cache.
21676      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21677      * Returning <em>false</em> aborts and exits the iteration.
21678      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21679      */
21680     each : function(fn, scope){
21681         this.data.each(fn, scope);
21682     },
21683
21684     /**
21685      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21686      * (e.g., during paging).
21687      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21688      */
21689     getModifiedRecords : function(){
21690         return this.modified;
21691     },
21692
21693     // private
21694     createFilterFn : function(property, value, anyMatch){
21695         if(!value.exec){ // not a regex
21696             value = String(value);
21697             if(value.length == 0){
21698                 return false;
21699             }
21700             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21701         }
21702         
21703         return function(r){
21704             return value.test(r.data[property]);
21705         };
21706     },
21707
21708     /**
21709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21710      * @param {String} property A field on your records
21711      * @param {Number} start The record index to start at (defaults to 0)
21712      * @param {Number} end The last record index to include (defaults to length - 1)
21713      * @return {Number} The sum
21714      */
21715     sum : function(property, start, end){
21716         var rs = this.data.items, v = 0;
21717         start = start || 0;
21718         end = (end || end === 0) ? end : rs.length-1;
21719
21720         for(var i = start; i <= end; i++){
21721             v += (rs[i].data[property] || 0);
21722         }
21723         return v;
21724     },
21725
21726     /**
21727      * Filter the records by a specified property.
21728      * @param {String} field A field on your records
21729      * @param {String/RegExp} value Either a string that the field
21730      * should start with or a RegExp to test against the field
21731      * @param {Boolean} anyMatch True to match any part not just the beginning
21732      */
21733     filter : function(property, value, anyMatch){
21734         
21735         if(typeof(property) == 'string'){
21736             var fn = this.createFilterFn(property, value, anyMatch);
21737             return fn ? this.filterBy(fn) : this.clearFilter();
21738         }
21739         
21740         var fn = [];
21741         var afn = [];
21742         
21743         var _this = this;
21744         
21745         Roo.each(property, function(p){
21746             if(anyMatch == true){
21747                 afn.push(_this.createFilterFn(p, value, true));
21748             }
21749             
21750             fn.push(_this.createFilterFn(p, value, false));
21751         });
21752         
21753         if(!fn.length && !afn.length){
21754             return this.clearFilter();
21755         }
21756         
21757         this.snapshot = this.snapshot || this.data;
21758         
21759         var filterData = [];
21760         
21761         Roo.each(fn, function(f){
21762             filterData.push(_this.queryBy(f, _this));
21763         });
21764         
21765         Roo.each(afn, function(f){
21766             filterData.push(_this.queryBy(f, _this));
21767         });
21768         
21769         var data = this.snapshot || this.data;
21770         
21771         var r = new Roo.util.MixedCollection();
21772         r.getKey = data.getKey;
21773         
21774         var keys =[];
21775         
21776         Roo.each(filterData, function(d){
21777             var k = d.keys, it = d.items;
21778             for(var i = 0, len = it.length; i < len; i++){
21779                 if(keys.indexOf(k[i]) == -1){
21780                     r.add(k[i], it[i]);
21781                 }
21782             }
21783         });
21784         
21785         this.data = r;
21786         this.fireEvent("datachanged", this);
21787     },
21788
21789     /**
21790      * Filter by a function. The specified function will be called with each
21791      * record in this data source. If the function returns true the record is included,
21792      * otherwise it is filtered.
21793      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21794      * @param {Object} scope (optional) The scope of the function (defaults to this)
21795      */
21796     filterBy : function(fn, scope){
21797         this.snapshot = this.snapshot || this.data;
21798         this.data = this.queryBy(fn, scope||this);
21799         this.fireEvent("datachanged", this);
21800     },
21801
21802     /**
21803      * Query the records by a specified property.
21804      * @param {String} field A field on your records
21805      * @param {String/RegExp} value Either a string that the field
21806      * should start with or a RegExp to test against the field
21807      * @param {Boolean} anyMatch True to match any part not just the beginning
21808      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21809      */
21810     query : function(property, value, anyMatch){
21811         var fn = this.createFilterFn(property, value, anyMatch);
21812         return fn ? this.queryBy(fn) : this.data.clone();
21813     },
21814
21815     /**
21816      * Query by a function. The specified function will be called with each
21817      * record in this data source. If the function returns true the record is included
21818      * in the results.
21819      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21820      * @param {Object} scope (optional) The scope of the function (defaults to this)
21821       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21822      **/
21823     queryBy : function(fn, scope){
21824         var data = this.snapshot || this.data;
21825         return data.filterBy(fn, scope||this);
21826     },
21827
21828     /**
21829      * Collects unique values for a particular dataIndex from this store.
21830      * @param {String} dataIndex The property to collect
21831      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21832      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21833      * @return {Array} An array of the unique values
21834      **/
21835     collect : function(dataIndex, allowNull, bypassFilter){
21836         var d = (bypassFilter === true && this.snapshot) ?
21837                 this.snapshot.items : this.data.items;
21838         var v, sv, r = [], l = {};
21839         for(var i = 0, len = d.length; i < len; i++){
21840             v = d[i].data[dataIndex];
21841             sv = String(v);
21842             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21843                 l[sv] = true;
21844                 r[r.length] = v;
21845             }
21846         }
21847         return r;
21848     },
21849
21850     /**
21851      * Revert to a view of the Record cache with no filtering applied.
21852      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21853      */
21854     clearFilter : function(suppressEvent){
21855         if(this.snapshot && this.snapshot != this.data){
21856             this.data = this.snapshot;
21857             delete this.snapshot;
21858             if(suppressEvent !== true){
21859                 this.fireEvent("datachanged", this);
21860             }
21861         }
21862     },
21863
21864     // private
21865     afterEdit : function(record){
21866         if(this.modified.indexOf(record) == -1){
21867             this.modified.push(record);
21868         }
21869         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21870     },
21871     
21872     // private
21873     afterReject : function(record){
21874         this.modified.remove(record);
21875         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21876     },
21877
21878     // private
21879     afterCommit : function(record){
21880         this.modified.remove(record);
21881         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21882     },
21883
21884     /**
21885      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21886      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21887      */
21888     commitChanges : function(){
21889         var m = this.modified.slice(0);
21890         this.modified = [];
21891         for(var i = 0, len = m.length; i < len; i++){
21892             m[i].commit();
21893         }
21894     },
21895
21896     /**
21897      * Cancel outstanding changes on all changed records.
21898      */
21899     rejectChanges : function(){
21900         var m = this.modified.slice(0);
21901         this.modified = [];
21902         for(var i = 0, len = m.length; i < len; i++){
21903             m[i].reject();
21904         }
21905     },
21906
21907     onMetaChange : function(meta, rtype, o){
21908         this.recordType = rtype;
21909         this.fields = rtype.prototype.fields;
21910         delete this.snapshot;
21911         this.sortInfo = meta.sortInfo || this.sortInfo;
21912         this.modified = [];
21913         this.fireEvent('metachange', this, this.reader.meta);
21914     },
21915     
21916     moveIndex : function(data, type)
21917     {
21918         var index = this.indexOf(data);
21919         
21920         var newIndex = index + type;
21921         
21922         this.remove(data);
21923         
21924         this.insert(newIndex, data);
21925         
21926     }
21927 });/*
21928  * Based on:
21929  * Ext JS Library 1.1.1
21930  * Copyright(c) 2006-2007, Ext JS, LLC.
21931  *
21932  * Originally Released Under LGPL - original licence link has changed is not relivant.
21933  *
21934  * Fork - LGPL
21935  * <script type="text/javascript">
21936  */
21937
21938 /**
21939  * @class Roo.data.SimpleStore
21940  * @extends Roo.data.Store
21941  * Small helper class to make creating Stores from Array data easier.
21942  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21943  * @cfg {Array} fields An array of field definition objects, or field name strings.
21944  * @cfg {Array} data The multi-dimensional array of data
21945  * @constructor
21946  * @param {Object} config
21947  */
21948 Roo.data.SimpleStore = function(config){
21949     Roo.data.SimpleStore.superclass.constructor.call(this, {
21950         isLocal : true,
21951         reader: new Roo.data.ArrayReader({
21952                 id: config.id
21953             },
21954             Roo.data.Record.create(config.fields)
21955         ),
21956         proxy : new Roo.data.MemoryProxy(config.data)
21957     });
21958     this.load();
21959 };
21960 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21961  * Based on:
21962  * Ext JS Library 1.1.1
21963  * Copyright(c) 2006-2007, Ext JS, LLC.
21964  *
21965  * Originally Released Under LGPL - original licence link has changed is not relivant.
21966  *
21967  * Fork - LGPL
21968  * <script type="text/javascript">
21969  */
21970
21971 /**
21972 /**
21973  * @extends Roo.data.Store
21974  * @class Roo.data.JsonStore
21975  * Small helper class to make creating Stores for JSON data easier. <br/>
21976 <pre><code>
21977 var store = new Roo.data.JsonStore({
21978     url: 'get-images.php',
21979     root: 'images',
21980     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21981 });
21982 </code></pre>
21983  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21984  * JsonReader and HttpProxy (unless inline data is provided).</b>
21985  * @cfg {Array} fields An array of field definition objects, or field name strings.
21986  * @constructor
21987  * @param {Object} config
21988  */
21989 Roo.data.JsonStore = function(c){
21990     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21991         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21992         reader: new Roo.data.JsonReader(c, c.fields)
21993     }));
21994 };
21995 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21996  * Based on:
21997  * Ext JS Library 1.1.1
21998  * Copyright(c) 2006-2007, Ext JS, LLC.
21999  *
22000  * Originally Released Under LGPL - original licence link has changed is not relivant.
22001  *
22002  * Fork - LGPL
22003  * <script type="text/javascript">
22004  */
22005
22006  
22007 Roo.data.Field = function(config){
22008     if(typeof config == "string"){
22009         config = {name: config};
22010     }
22011     Roo.apply(this, config);
22012     
22013     if(!this.type){
22014         this.type = "auto";
22015     }
22016     
22017     var st = Roo.data.SortTypes;
22018     // named sortTypes are supported, here we look them up
22019     if(typeof this.sortType == "string"){
22020         this.sortType = st[this.sortType];
22021     }
22022     
22023     // set default sortType for strings and dates
22024     if(!this.sortType){
22025         switch(this.type){
22026             case "string":
22027                 this.sortType = st.asUCString;
22028                 break;
22029             case "date":
22030                 this.sortType = st.asDate;
22031                 break;
22032             default:
22033                 this.sortType = st.none;
22034         }
22035     }
22036
22037     // define once
22038     var stripRe = /[\$,%]/g;
22039
22040     // prebuilt conversion function for this field, instead of
22041     // switching every time we're reading a value
22042     if(!this.convert){
22043         var cv, dateFormat = this.dateFormat;
22044         switch(this.type){
22045             case "":
22046             case "auto":
22047             case undefined:
22048                 cv = function(v){ return v; };
22049                 break;
22050             case "string":
22051                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22052                 break;
22053             case "int":
22054                 cv = function(v){
22055                     return v !== undefined && v !== null && v !== '' ?
22056                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22057                     };
22058                 break;
22059             case "float":
22060                 cv = function(v){
22061                     return v !== undefined && v !== null && v !== '' ?
22062                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22063                     };
22064                 break;
22065             case "bool":
22066             case "boolean":
22067                 cv = function(v){ return v === true || v === "true" || v == 1; };
22068                 break;
22069             case "date":
22070                 cv = function(v){
22071                     if(!v){
22072                         return '';
22073                     }
22074                     if(v instanceof Date){
22075                         return v;
22076                     }
22077                     if(dateFormat){
22078                         if(dateFormat == "timestamp"){
22079                             return new Date(v*1000);
22080                         }
22081                         return Date.parseDate(v, dateFormat);
22082                     }
22083                     var parsed = Date.parse(v);
22084                     return parsed ? new Date(parsed) : null;
22085                 };
22086              break;
22087             
22088         }
22089         this.convert = cv;
22090     }
22091 };
22092
22093 Roo.data.Field.prototype = {
22094     dateFormat: null,
22095     defaultValue: "",
22096     mapping: null,
22097     sortType : null,
22098     sortDir : "ASC"
22099 };/*
22100  * Based on:
22101  * Ext JS Library 1.1.1
22102  * Copyright(c) 2006-2007, Ext JS, LLC.
22103  *
22104  * Originally Released Under LGPL - original licence link has changed is not relivant.
22105  *
22106  * Fork - LGPL
22107  * <script type="text/javascript">
22108  */
22109  
22110 // Base class for reading structured data from a data source.  This class is intended to be
22111 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22112
22113 /**
22114  * @class Roo.data.DataReader
22115  * Base class for reading structured data from a data source.  This class is intended to be
22116  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22117  */
22118
22119 Roo.data.DataReader = function(meta, recordType){
22120     
22121     this.meta = meta;
22122     
22123     this.recordType = recordType instanceof Array ? 
22124         Roo.data.Record.create(recordType) : recordType;
22125 };
22126
22127 Roo.data.DataReader.prototype = {
22128      /**
22129      * Create an empty record
22130      * @param {Object} data (optional) - overlay some values
22131      * @return {Roo.data.Record} record created.
22132      */
22133     newRow :  function(d) {
22134         var da =  {};
22135         this.recordType.prototype.fields.each(function(c) {
22136             switch( c.type) {
22137                 case 'int' : da[c.name] = 0; break;
22138                 case 'date' : da[c.name] = new Date(); break;
22139                 case 'float' : da[c.name] = 0.0; break;
22140                 case 'boolean' : da[c.name] = false; break;
22141                 default : da[c.name] = ""; break;
22142             }
22143             
22144         });
22145         return new this.recordType(Roo.apply(da, d));
22146     }
22147     
22148 };/*
22149  * Based on:
22150  * Ext JS Library 1.1.1
22151  * Copyright(c) 2006-2007, Ext JS, LLC.
22152  *
22153  * Originally Released Under LGPL - original licence link has changed is not relivant.
22154  *
22155  * Fork - LGPL
22156  * <script type="text/javascript">
22157  */
22158
22159 /**
22160  * @class Roo.data.DataProxy
22161  * @extends Roo.data.Observable
22162  * This class is an abstract base class for implementations which provide retrieval of
22163  * unformatted data objects.<br>
22164  * <p>
22165  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22166  * (of the appropriate type which knows how to parse the data object) to provide a block of
22167  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22168  * <p>
22169  * Custom implementations must implement the load method as described in
22170  * {@link Roo.data.HttpProxy#load}.
22171  */
22172 Roo.data.DataProxy = function(){
22173     this.addEvents({
22174         /**
22175          * @event beforeload
22176          * Fires before a network request is made to retrieve a data object.
22177          * @param {Object} This DataProxy object.
22178          * @param {Object} params The params parameter to the load function.
22179          */
22180         beforeload : true,
22181         /**
22182          * @event load
22183          * Fires before the load method's callback is called.
22184          * @param {Object} This DataProxy object.
22185          * @param {Object} o The data object.
22186          * @param {Object} arg The callback argument object passed to the load function.
22187          */
22188         load : true,
22189         /**
22190          * @event loadexception
22191          * Fires if an Exception occurs during data retrieval.
22192          * @param {Object} This DataProxy object.
22193          * @param {Object} o The data object.
22194          * @param {Object} arg The callback argument object passed to the load function.
22195          * @param {Object} e The Exception.
22196          */
22197         loadexception : true
22198     });
22199     Roo.data.DataProxy.superclass.constructor.call(this);
22200 };
22201
22202 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22203
22204     /**
22205      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22206      */
22207 /*
22208  * Based on:
22209  * Ext JS Library 1.1.1
22210  * Copyright(c) 2006-2007, Ext JS, LLC.
22211  *
22212  * Originally Released Under LGPL - original licence link has changed is not relivant.
22213  *
22214  * Fork - LGPL
22215  * <script type="text/javascript">
22216  */
22217 /**
22218  * @class Roo.data.MemoryProxy
22219  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22220  * to the Reader when its load method is called.
22221  * @constructor
22222  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22223  */
22224 Roo.data.MemoryProxy = function(data){
22225     if (data.data) {
22226         data = data.data;
22227     }
22228     Roo.data.MemoryProxy.superclass.constructor.call(this);
22229     this.data = data;
22230 };
22231
22232 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22233     /**
22234      * Load data from the requested source (in this case an in-memory
22235      * data object passed to the constructor), read the data object into
22236      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22237      * process that block using the passed callback.
22238      * @param {Object} params This parameter is not used by the MemoryProxy class.
22239      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22240      * object into a block of Roo.data.Records.
22241      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22242      * The function must be passed <ul>
22243      * <li>The Record block object</li>
22244      * <li>The "arg" argument from the load function</li>
22245      * <li>A boolean success indicator</li>
22246      * </ul>
22247      * @param {Object} scope The scope in which to call the callback
22248      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22249      */
22250     load : function(params, reader, callback, scope, arg){
22251         params = params || {};
22252         var result;
22253         try {
22254             result = reader.readRecords(this.data);
22255         }catch(e){
22256             this.fireEvent("loadexception", this, arg, null, e);
22257             callback.call(scope, null, arg, false);
22258             return;
22259         }
22260         callback.call(scope, result, arg, true);
22261     },
22262     
22263     // private
22264     update : function(params, records){
22265         
22266     }
22267 });/*
22268  * Based on:
22269  * Ext JS Library 1.1.1
22270  * Copyright(c) 2006-2007, Ext JS, LLC.
22271  *
22272  * Originally Released Under LGPL - original licence link has changed is not relivant.
22273  *
22274  * Fork - LGPL
22275  * <script type="text/javascript">
22276  */
22277 /**
22278  * @class Roo.data.HttpProxy
22279  * @extends Roo.data.DataProxy
22280  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22281  * configured to reference a certain URL.<br><br>
22282  * <p>
22283  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22284  * from which the running page was served.<br><br>
22285  * <p>
22286  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22287  * <p>
22288  * Be aware that to enable the browser to parse an XML document, the server must set
22289  * the Content-Type header in the HTTP response to "text/xml".
22290  * @constructor
22291  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22292  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22293  * will be used to make the request.
22294  */
22295 Roo.data.HttpProxy = function(conn){
22296     Roo.data.HttpProxy.superclass.constructor.call(this);
22297     // is conn a conn config or a real conn?
22298     this.conn = conn;
22299     this.useAjax = !conn || !conn.events;
22300   
22301 };
22302
22303 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22304     // thse are take from connection...
22305     
22306     /**
22307      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22308      */
22309     /**
22310      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22311      * extra parameters to each request made by this object. (defaults to undefined)
22312      */
22313     /**
22314      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22315      *  to each request made by this object. (defaults to undefined)
22316      */
22317     /**
22318      * @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)
22319      */
22320     /**
22321      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22322      */
22323      /**
22324      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22325      * @type Boolean
22326      */
22327   
22328
22329     /**
22330      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22331      * @type Boolean
22332      */
22333     /**
22334      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22335      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22336      * a finer-grained basis than the DataProxy events.
22337      */
22338     getConnection : function(){
22339         return this.useAjax ? Roo.Ajax : this.conn;
22340     },
22341
22342     /**
22343      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22344      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22345      * process that block using the passed callback.
22346      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22347      * for the request to the remote server.
22348      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22349      * object into a block of Roo.data.Records.
22350      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22351      * The function must be passed <ul>
22352      * <li>The Record block object</li>
22353      * <li>The "arg" argument from the load function</li>
22354      * <li>A boolean success indicator</li>
22355      * </ul>
22356      * @param {Object} scope The scope in which to call the callback
22357      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22358      */
22359     load : function(params, reader, callback, scope, arg){
22360         if(this.fireEvent("beforeload", this, params) !== false){
22361             var  o = {
22362                 params : params || {},
22363                 request: {
22364                     callback : callback,
22365                     scope : scope,
22366                     arg : arg
22367                 },
22368                 reader: reader,
22369                 callback : this.loadResponse,
22370                 scope: this
22371             };
22372             if(this.useAjax){
22373                 Roo.applyIf(o, this.conn);
22374                 if(this.activeRequest){
22375                     Roo.Ajax.abort(this.activeRequest);
22376                 }
22377                 this.activeRequest = Roo.Ajax.request(o);
22378             }else{
22379                 this.conn.request(o);
22380             }
22381         }else{
22382             callback.call(scope||this, null, arg, false);
22383         }
22384     },
22385
22386     // private
22387     loadResponse : function(o, success, response){
22388         delete this.activeRequest;
22389         if(!success){
22390             this.fireEvent("loadexception", this, o, response);
22391             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22392             return;
22393         }
22394         var result;
22395         try {
22396             result = o.reader.read(response);
22397         }catch(e){
22398             this.fireEvent("loadexception", this, o, response, e);
22399             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22400             return;
22401         }
22402         
22403         this.fireEvent("load", this, o, o.request.arg);
22404         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22405     },
22406
22407     // private
22408     update : function(dataSet){
22409
22410     },
22411
22412     // private
22413     updateResponse : function(dataSet){
22414
22415     }
22416 });/*
22417  * Based on:
22418  * Ext JS Library 1.1.1
22419  * Copyright(c) 2006-2007, Ext JS, LLC.
22420  *
22421  * Originally Released Under LGPL - original licence link has changed is not relivant.
22422  *
22423  * Fork - LGPL
22424  * <script type="text/javascript">
22425  */
22426
22427 /**
22428  * @class Roo.data.ScriptTagProxy
22429  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22430  * other than the originating domain of the running page.<br><br>
22431  * <p>
22432  * <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
22433  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22434  * <p>
22435  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22436  * source code that is used as the source inside a &lt;script> tag.<br><br>
22437  * <p>
22438  * In order for the browser to process the returned data, the server must wrap the data object
22439  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22440  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22441  * depending on whether the callback name was passed:
22442  * <p>
22443  * <pre><code>
22444 boolean scriptTag = false;
22445 String cb = request.getParameter("callback");
22446 if (cb != null) {
22447     scriptTag = true;
22448     response.setContentType("text/javascript");
22449 } else {
22450     response.setContentType("application/x-json");
22451 }
22452 Writer out = response.getWriter();
22453 if (scriptTag) {
22454     out.write(cb + "(");
22455 }
22456 out.print(dataBlock.toJsonString());
22457 if (scriptTag) {
22458     out.write(");");
22459 }
22460 </pre></code>
22461  *
22462  * @constructor
22463  * @param {Object} config A configuration object.
22464  */
22465 Roo.data.ScriptTagProxy = function(config){
22466     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22467     Roo.apply(this, config);
22468     this.head = document.getElementsByTagName("head")[0];
22469 };
22470
22471 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22472
22473 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22474     /**
22475      * @cfg {String} url The URL from which to request the data object.
22476      */
22477     /**
22478      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22479      */
22480     timeout : 30000,
22481     /**
22482      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22483      * the server the name of the callback function set up by the load call to process the returned data object.
22484      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22485      * javascript output which calls this named function passing the data object as its only parameter.
22486      */
22487     callbackParam : "callback",
22488     /**
22489      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22490      * name to the request.
22491      */
22492     nocache : true,
22493
22494     /**
22495      * Load data from the configured URL, read the data object into
22496      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22497      * process that block using the passed callback.
22498      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22499      * for the request to the remote server.
22500      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22501      * object into a block of Roo.data.Records.
22502      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22503      * The function must be passed <ul>
22504      * <li>The Record block object</li>
22505      * <li>The "arg" argument from the load function</li>
22506      * <li>A boolean success indicator</li>
22507      * </ul>
22508      * @param {Object} scope The scope in which to call the callback
22509      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22510      */
22511     load : function(params, reader, callback, scope, arg){
22512         if(this.fireEvent("beforeload", this, params) !== false){
22513
22514             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22515
22516             var url = this.url;
22517             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22518             if(this.nocache){
22519                 url += "&_dc=" + (new Date().getTime());
22520             }
22521             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22522             var trans = {
22523                 id : transId,
22524                 cb : "stcCallback"+transId,
22525                 scriptId : "stcScript"+transId,
22526                 params : params,
22527                 arg : arg,
22528                 url : url,
22529                 callback : callback,
22530                 scope : scope,
22531                 reader : reader
22532             };
22533             var conn = this;
22534
22535             window[trans.cb] = function(o){
22536                 conn.handleResponse(o, trans);
22537             };
22538
22539             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22540
22541             if(this.autoAbort !== false){
22542                 this.abort();
22543             }
22544
22545             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22546
22547             var script = document.createElement("script");
22548             script.setAttribute("src", url);
22549             script.setAttribute("type", "text/javascript");
22550             script.setAttribute("id", trans.scriptId);
22551             this.head.appendChild(script);
22552
22553             this.trans = trans;
22554         }else{
22555             callback.call(scope||this, null, arg, false);
22556         }
22557     },
22558
22559     // private
22560     isLoading : function(){
22561         return this.trans ? true : false;
22562     },
22563
22564     /**
22565      * Abort the current server request.
22566      */
22567     abort : function(){
22568         if(this.isLoading()){
22569             this.destroyTrans(this.trans);
22570         }
22571     },
22572
22573     // private
22574     destroyTrans : function(trans, isLoaded){
22575         this.head.removeChild(document.getElementById(trans.scriptId));
22576         clearTimeout(trans.timeoutId);
22577         if(isLoaded){
22578             window[trans.cb] = undefined;
22579             try{
22580                 delete window[trans.cb];
22581             }catch(e){}
22582         }else{
22583             // if hasn't been loaded, wait for load to remove it to prevent script error
22584             window[trans.cb] = function(){
22585                 window[trans.cb] = undefined;
22586                 try{
22587                     delete window[trans.cb];
22588                 }catch(e){}
22589             };
22590         }
22591     },
22592
22593     // private
22594     handleResponse : function(o, trans){
22595         this.trans = false;
22596         this.destroyTrans(trans, true);
22597         var result;
22598         try {
22599             result = trans.reader.readRecords(o);
22600         }catch(e){
22601             this.fireEvent("loadexception", this, o, trans.arg, e);
22602             trans.callback.call(trans.scope||window, null, trans.arg, false);
22603             return;
22604         }
22605         this.fireEvent("load", this, o, trans.arg);
22606         trans.callback.call(trans.scope||window, result, trans.arg, true);
22607     },
22608
22609     // private
22610     handleFailure : function(trans){
22611         this.trans = false;
22612         this.destroyTrans(trans, false);
22613         this.fireEvent("loadexception", this, null, trans.arg);
22614         trans.callback.call(trans.scope||window, null, trans.arg, false);
22615     }
22616 });/*
22617  * Based on:
22618  * Ext JS Library 1.1.1
22619  * Copyright(c) 2006-2007, Ext JS, LLC.
22620  *
22621  * Originally Released Under LGPL - original licence link has changed is not relivant.
22622  *
22623  * Fork - LGPL
22624  * <script type="text/javascript">
22625  */
22626
22627 /**
22628  * @class Roo.data.JsonReader
22629  * @extends Roo.data.DataReader
22630  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22631  * based on mappings in a provided Roo.data.Record constructor.
22632  * 
22633  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22634  * in the reply previously. 
22635  * 
22636  * <p>
22637  * Example code:
22638  * <pre><code>
22639 var RecordDef = Roo.data.Record.create([
22640     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22641     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22642 ]);
22643 var myReader = new Roo.data.JsonReader({
22644     totalProperty: "results",    // The property which contains the total dataset size (optional)
22645     root: "rows",                // The property which contains an Array of row objects
22646     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22647 }, RecordDef);
22648 </code></pre>
22649  * <p>
22650  * This would consume a JSON file like this:
22651  * <pre><code>
22652 { 'results': 2, 'rows': [
22653     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22654     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22655 }
22656 </code></pre>
22657  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22658  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22659  * paged from the remote server.
22660  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22661  * @cfg {String} root name of the property which contains the Array of row objects.
22662  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22663  * @constructor
22664  * Create a new JsonReader
22665  * @param {Object} meta Metadata configuration options
22666  * @param {Object} recordType Either an Array of field definition objects,
22667  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22668  */
22669 Roo.data.JsonReader = function(meta, recordType){
22670     
22671     meta = meta || {};
22672     // set some defaults:
22673     Roo.applyIf(meta, {
22674         totalProperty: 'total',
22675         successProperty : 'success',
22676         root : 'data',
22677         id : 'id'
22678     });
22679     
22680     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22681 };
22682 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22683     
22684     /**
22685      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22686      * Used by Store query builder to append _requestMeta to params.
22687      * 
22688      */
22689     metaFromRemote : false,
22690     /**
22691      * This method is only used by a DataProxy which has retrieved data from a remote server.
22692      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22693      * @return {Object} data A data block which is used by an Roo.data.Store object as
22694      * a cache of Roo.data.Records.
22695      */
22696     read : function(response){
22697         var json = response.responseText;
22698        
22699         var o = /* eval:var:o */ eval("("+json+")");
22700         if(!o) {
22701             throw {message: "JsonReader.read: Json object not found"};
22702         }
22703         
22704         if(o.metaData){
22705             
22706             delete this.ef;
22707             this.metaFromRemote = true;
22708             this.meta = o.metaData;
22709             this.recordType = Roo.data.Record.create(o.metaData.fields);
22710             this.onMetaChange(this.meta, this.recordType, o);
22711         }
22712         return this.readRecords(o);
22713     },
22714
22715     // private function a store will implement
22716     onMetaChange : function(meta, recordType, o){
22717
22718     },
22719
22720     /**
22721          * @ignore
22722          */
22723     simpleAccess: function(obj, subsc) {
22724         return obj[subsc];
22725     },
22726
22727         /**
22728          * @ignore
22729          */
22730     getJsonAccessor: function(){
22731         var re = /[\[\.]/;
22732         return function(expr) {
22733             try {
22734                 return(re.test(expr))
22735                     ? new Function("obj", "return obj." + expr)
22736                     : function(obj){
22737                         return obj[expr];
22738                     };
22739             } catch(e){}
22740             return Roo.emptyFn;
22741         };
22742     }(),
22743
22744     /**
22745      * Create a data block containing Roo.data.Records from an XML document.
22746      * @param {Object} o An object which contains an Array of row objects in the property specified
22747      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22748      * which contains the total size of the dataset.
22749      * @return {Object} data A data block which is used by an Roo.data.Store object as
22750      * a cache of Roo.data.Records.
22751      */
22752     readRecords : function(o){
22753         /**
22754          * After any data loads, the raw JSON data is available for further custom processing.
22755          * @type Object
22756          */
22757         this.o = o;
22758         var s = this.meta, Record = this.recordType,
22759             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22760
22761 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22762         if (!this.ef) {
22763             if(s.totalProperty) {
22764                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22765                 }
22766                 if(s.successProperty) {
22767                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22768                 }
22769                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22770                 if (s.id) {
22771                         var g = this.getJsonAccessor(s.id);
22772                         this.getId = function(rec) {
22773                                 var r = g(rec);  
22774                                 return (r === undefined || r === "") ? null : r;
22775                         };
22776                 } else {
22777                         this.getId = function(){return null;};
22778                 }
22779             this.ef = [];
22780             for(var jj = 0; jj < fl; jj++){
22781                 f = fi[jj];
22782                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22783                 this.ef[jj] = this.getJsonAccessor(map);
22784             }
22785         }
22786
22787         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22788         if(s.totalProperty){
22789             var vt = parseInt(this.getTotal(o), 10);
22790             if(!isNaN(vt)){
22791                 totalRecords = vt;
22792             }
22793         }
22794         if(s.successProperty){
22795             var vs = this.getSuccess(o);
22796             if(vs === false || vs === 'false'){
22797                 success = false;
22798             }
22799         }
22800         var records = [];
22801         for(var i = 0; i < c; i++){
22802                 var n = root[i];
22803             var values = {};
22804             var id = this.getId(n);
22805             for(var j = 0; j < fl; j++){
22806                 f = fi[j];
22807             var v = this.ef[j](n);
22808             if (!f.convert) {
22809                 Roo.log('missing convert for ' + f.name);
22810                 Roo.log(f);
22811                 continue;
22812             }
22813             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22814             }
22815             var record = new Record(values, id);
22816             record.json = n;
22817             records[i] = record;
22818         }
22819         return {
22820             raw : o,
22821             success : success,
22822             records : records,
22823             totalRecords : totalRecords
22824         };
22825     }
22826 });/*
22827  * Based on:
22828  * Ext JS Library 1.1.1
22829  * Copyright(c) 2006-2007, Ext JS, LLC.
22830  *
22831  * Originally Released Under LGPL - original licence link has changed is not relivant.
22832  *
22833  * Fork - LGPL
22834  * <script type="text/javascript">
22835  */
22836
22837 /**
22838  * @class Roo.data.XmlReader
22839  * @extends Roo.data.DataReader
22840  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22841  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22842  * <p>
22843  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22844  * header in the HTTP response must be set to "text/xml".</em>
22845  * <p>
22846  * Example code:
22847  * <pre><code>
22848 var RecordDef = Roo.data.Record.create([
22849    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22850    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22851 ]);
22852 var myReader = new Roo.data.XmlReader({
22853    totalRecords: "results", // The element which contains the total dataset size (optional)
22854    record: "row",           // The repeated element which contains row information
22855    id: "id"                 // The element within the row that provides an ID for the record (optional)
22856 }, RecordDef);
22857 </code></pre>
22858  * <p>
22859  * This would consume an XML file like this:
22860  * <pre><code>
22861 &lt;?xml?>
22862 &lt;dataset>
22863  &lt;results>2&lt;/results>
22864  &lt;row>
22865    &lt;id>1&lt;/id>
22866    &lt;name>Bill&lt;/name>
22867    &lt;occupation>Gardener&lt;/occupation>
22868  &lt;/row>
22869  &lt;row>
22870    &lt;id>2&lt;/id>
22871    &lt;name>Ben&lt;/name>
22872    &lt;occupation>Horticulturalist&lt;/occupation>
22873  &lt;/row>
22874 &lt;/dataset>
22875 </code></pre>
22876  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22877  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22878  * paged from the remote server.
22879  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22880  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22881  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22882  * a record identifier value.
22883  * @constructor
22884  * Create a new XmlReader
22885  * @param {Object} meta Metadata configuration options
22886  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22887  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22888  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22889  */
22890 Roo.data.XmlReader = function(meta, recordType){
22891     meta = meta || {};
22892     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22893 };
22894 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22895     /**
22896      * This method is only used by a DataProxy which has retrieved data from a remote server.
22897          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22898          * to contain a method called 'responseXML' that returns an XML document object.
22899      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22900      * a cache of Roo.data.Records.
22901      */
22902     read : function(response){
22903         var doc = response.responseXML;
22904         if(!doc) {
22905             throw {message: "XmlReader.read: XML Document not available"};
22906         }
22907         return this.readRecords(doc);
22908     },
22909
22910     /**
22911      * Create a data block containing Roo.data.Records from an XML document.
22912          * @param {Object} doc A parsed XML document.
22913      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22914      * a cache of Roo.data.Records.
22915      */
22916     readRecords : function(doc){
22917         /**
22918          * After any data loads/reads, the raw XML Document is available for further custom processing.
22919          * @type XMLDocument
22920          */
22921         this.xmlData = doc;
22922         var root = doc.documentElement || doc;
22923         var q = Roo.DomQuery;
22924         var recordType = this.recordType, fields = recordType.prototype.fields;
22925         var sid = this.meta.id;
22926         var totalRecords = 0, success = true;
22927         if(this.meta.totalRecords){
22928             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22929         }
22930         
22931         if(this.meta.success){
22932             var sv = q.selectValue(this.meta.success, root, true);
22933             success = sv !== false && sv !== 'false';
22934         }
22935         var records = [];
22936         var ns = q.select(this.meta.record, root);
22937         for(var i = 0, len = ns.length; i < len; i++) {
22938                 var n = ns[i];
22939                 var values = {};
22940                 var id = sid ? q.selectValue(sid, n) : undefined;
22941                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22942                     var f = fields.items[j];
22943                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22944                     v = f.convert(v);
22945                     values[f.name] = v;
22946                 }
22947                 var record = new recordType(values, id);
22948                 record.node = n;
22949                 records[records.length] = record;
22950             }
22951
22952             return {
22953                 success : success,
22954                 records : records,
22955                 totalRecords : totalRecords || records.length
22956             };
22957     }
22958 });/*
22959  * Based on:
22960  * Ext JS Library 1.1.1
22961  * Copyright(c) 2006-2007, Ext JS, LLC.
22962  *
22963  * Originally Released Under LGPL - original licence link has changed is not relivant.
22964  *
22965  * Fork - LGPL
22966  * <script type="text/javascript">
22967  */
22968
22969 /**
22970  * @class Roo.data.ArrayReader
22971  * @extends Roo.data.DataReader
22972  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22973  * Each element of that Array represents a row of data fields. The
22974  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22975  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22976  * <p>
22977  * Example code:.
22978  * <pre><code>
22979 var RecordDef = Roo.data.Record.create([
22980     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22981     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22982 ]);
22983 var myReader = new Roo.data.ArrayReader({
22984     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22985 }, RecordDef);
22986 </code></pre>
22987  * <p>
22988  * This would consume an Array like this:
22989  * <pre><code>
22990 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22991   </code></pre>
22992  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22993  * @constructor
22994  * Create a new JsonReader
22995  * @param {Object} meta Metadata configuration options.
22996  * @param {Object} recordType Either an Array of field definition objects
22997  * as specified to {@link Roo.data.Record#create},
22998  * or an {@link Roo.data.Record} object
22999  * created using {@link Roo.data.Record#create}.
23000  */
23001 Roo.data.ArrayReader = function(meta, recordType){
23002     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
23003 };
23004
23005 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
23006     /**
23007      * Create a data block containing Roo.data.Records from an XML document.
23008      * @param {Object} o An Array of row objects which represents the dataset.
23009      * @return {Object} data A data block which is used by an Roo.data.Store object as
23010      * a cache of Roo.data.Records.
23011      */
23012     readRecords : function(o){
23013         var sid = this.meta ? this.meta.id : null;
23014         var recordType = this.recordType, fields = recordType.prototype.fields;
23015         var records = [];
23016         var root = o;
23017             for(var i = 0; i < root.length; i++){
23018                     var n = root[i];
23019                 var values = {};
23020                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
23021                 for(var j = 0, jlen = fields.length; j < jlen; j++){
23022                 var f = fields.items[j];
23023                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
23024                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
23025                 v = f.convert(v);
23026                 values[f.name] = v;
23027             }
23028                 var record = new recordType(values, id);
23029                 record.json = n;
23030                 records[records.length] = record;
23031             }
23032             return {
23033                 records : records,
23034                 totalRecords : records.length
23035             };
23036     }
23037 });/*
23038  * Based on:
23039  * Ext JS Library 1.1.1
23040  * Copyright(c) 2006-2007, Ext JS, LLC.
23041  *
23042  * Originally Released Under LGPL - original licence link has changed is not relivant.
23043  *
23044  * Fork - LGPL
23045  * <script type="text/javascript">
23046  */
23047
23048
23049 /**
23050  * @class Roo.data.Tree
23051  * @extends Roo.util.Observable
23052  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23053  * in the tree have most standard DOM functionality.
23054  * @constructor
23055  * @param {Node} root (optional) The root node
23056  */
23057 Roo.data.Tree = function(root){
23058    this.nodeHash = {};
23059    /**
23060     * The root node for this tree
23061     * @type Node
23062     */
23063    this.root = null;
23064    if(root){
23065        this.setRootNode(root);
23066    }
23067    this.addEvents({
23068        /**
23069         * @event append
23070         * Fires when a new child node is appended to a node in this tree.
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} parent The parent node
23073         * @param {Node} node The newly appended node
23074         * @param {Number} index The index of the newly appended node
23075         */
23076        "append" : true,
23077        /**
23078         * @event remove
23079         * Fires when a child node is removed from a node in this tree.
23080         * @param {Tree} tree The owner tree
23081         * @param {Node} parent The parent node
23082         * @param {Node} node The child node removed
23083         */
23084        "remove" : true,
23085        /**
23086         * @event move
23087         * Fires when a node is moved to a new location in the tree
23088         * @param {Tree} tree The owner tree
23089         * @param {Node} node The node moved
23090         * @param {Node} oldParent The old parent of this node
23091         * @param {Node} newParent The new parent of this node
23092         * @param {Number} index The index it was moved to
23093         */
23094        "move" : true,
23095        /**
23096         * @event insert
23097         * Fires when a new child node is inserted in a node in this tree.
23098         * @param {Tree} tree The owner tree
23099         * @param {Node} parent The parent node
23100         * @param {Node} node The child node inserted
23101         * @param {Node} refNode The child node the node was inserted before
23102         */
23103        "insert" : true,
23104        /**
23105         * @event beforeappend
23106         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23107         * @param {Tree} tree The owner tree
23108         * @param {Node} parent The parent node
23109         * @param {Node} node The child node to be appended
23110         */
23111        "beforeappend" : true,
23112        /**
23113         * @event beforeremove
23114         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23115         * @param {Tree} tree The owner tree
23116         * @param {Node} parent The parent node
23117         * @param {Node} node The child node to be removed
23118         */
23119        "beforeremove" : true,
23120        /**
23121         * @event beforemove
23122         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23123         * @param {Tree} tree The owner tree
23124         * @param {Node} node The node being moved
23125         * @param {Node} oldParent The parent of the node
23126         * @param {Node} newParent The new parent the node is moving to
23127         * @param {Number} index The index it is being moved to
23128         */
23129        "beforemove" : true,
23130        /**
23131         * @event beforeinsert
23132         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23133         * @param {Tree} tree The owner tree
23134         * @param {Node} parent The parent node
23135         * @param {Node} node The child node to be inserted
23136         * @param {Node} refNode The child node the node is being inserted before
23137         */
23138        "beforeinsert" : true
23139    });
23140
23141     Roo.data.Tree.superclass.constructor.call(this);
23142 };
23143
23144 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23145     pathSeparator: "/",
23146
23147     proxyNodeEvent : function(){
23148         return this.fireEvent.apply(this, arguments);
23149     },
23150
23151     /**
23152      * Returns the root node for this tree.
23153      * @return {Node}
23154      */
23155     getRootNode : function(){
23156         return this.root;
23157     },
23158
23159     /**
23160      * Sets the root node for this tree.
23161      * @param {Node} node
23162      * @return {Node}
23163      */
23164     setRootNode : function(node){
23165         this.root = node;
23166         node.ownerTree = this;
23167         node.isRoot = true;
23168         this.registerNode(node);
23169         return node;
23170     },
23171
23172     /**
23173      * Gets a node in this tree by its id.
23174      * @param {String} id
23175      * @return {Node}
23176      */
23177     getNodeById : function(id){
23178         return this.nodeHash[id];
23179     },
23180
23181     registerNode : function(node){
23182         this.nodeHash[node.id] = node;
23183     },
23184
23185     unregisterNode : function(node){
23186         delete this.nodeHash[node.id];
23187     },
23188
23189     toString : function(){
23190         return "[Tree"+(this.id?" "+this.id:"")+"]";
23191     }
23192 });
23193
23194 /**
23195  * @class Roo.data.Node
23196  * @extends Roo.util.Observable
23197  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23198  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23199  * @constructor
23200  * @param {Object} attributes The attributes/config for the node
23201  */
23202 Roo.data.Node = function(attributes){
23203     /**
23204      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23205      * @type {Object}
23206      */
23207     this.attributes = attributes || {};
23208     this.leaf = this.attributes.leaf;
23209     /**
23210      * The node id. @type String
23211      */
23212     this.id = this.attributes.id;
23213     if(!this.id){
23214         this.id = Roo.id(null, "ynode-");
23215         this.attributes.id = this.id;
23216     }
23217      
23218     
23219     /**
23220      * All child nodes of this node. @type Array
23221      */
23222     this.childNodes = [];
23223     if(!this.childNodes.indexOf){ // indexOf is a must
23224         this.childNodes.indexOf = function(o){
23225             for(var i = 0, len = this.length; i < len; i++){
23226                 if(this[i] == o) {
23227                     return i;
23228                 }
23229             }
23230             return -1;
23231         };
23232     }
23233     /**
23234      * The parent node for this node. @type Node
23235      */
23236     this.parentNode = null;
23237     /**
23238      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23239      */
23240     this.firstChild = null;
23241     /**
23242      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23243      */
23244     this.lastChild = null;
23245     /**
23246      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23247      */
23248     this.previousSibling = null;
23249     /**
23250      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23251      */
23252     this.nextSibling = null;
23253
23254     this.addEvents({
23255        /**
23256         * @event append
23257         * Fires when a new child node is appended
23258         * @param {Tree} tree The owner tree
23259         * @param {Node} this This node
23260         * @param {Node} node The newly appended node
23261         * @param {Number} index The index of the newly appended node
23262         */
23263        "append" : true,
23264        /**
23265         * @event remove
23266         * Fires when a child node is removed
23267         * @param {Tree} tree The owner tree
23268         * @param {Node} this This node
23269         * @param {Node} node The removed node
23270         */
23271        "remove" : true,
23272        /**
23273         * @event move
23274         * Fires when this node is moved to a new location in the tree
23275         * @param {Tree} tree The owner tree
23276         * @param {Node} this This node
23277         * @param {Node} oldParent The old parent of this node
23278         * @param {Node} newParent The new parent of this node
23279         * @param {Number} index The index it was moved to
23280         */
23281        "move" : true,
23282        /**
23283         * @event insert
23284         * Fires when a new child node is inserted.
23285         * @param {Tree} tree The owner tree
23286         * @param {Node} this This node
23287         * @param {Node} node The child node inserted
23288         * @param {Node} refNode The child node the node was inserted before
23289         */
23290        "insert" : true,
23291        /**
23292         * @event beforeappend
23293         * Fires before a new child is appended, return false to cancel the append.
23294         * @param {Tree} tree The owner tree
23295         * @param {Node} this This node
23296         * @param {Node} node The child node to be appended
23297         */
23298        "beforeappend" : true,
23299        /**
23300         * @event beforeremove
23301         * Fires before a child is removed, return false to cancel the remove.
23302         * @param {Tree} tree The owner tree
23303         * @param {Node} this This node
23304         * @param {Node} node The child node to be removed
23305         */
23306        "beforeremove" : true,
23307        /**
23308         * @event beforemove
23309         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23310         * @param {Tree} tree The owner tree
23311         * @param {Node} this This node
23312         * @param {Node} oldParent The parent of this node
23313         * @param {Node} newParent The new parent this node is moving to
23314         * @param {Number} index The index it is being moved to
23315         */
23316        "beforemove" : true,
23317        /**
23318         * @event beforeinsert
23319         * Fires before a new child is inserted, return false to cancel the insert.
23320         * @param {Tree} tree The owner tree
23321         * @param {Node} this This node
23322         * @param {Node} node The child node to be inserted
23323         * @param {Node} refNode The child node the node is being inserted before
23324         */
23325        "beforeinsert" : true
23326    });
23327     this.listeners = this.attributes.listeners;
23328     Roo.data.Node.superclass.constructor.call(this);
23329 };
23330
23331 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23332     fireEvent : function(evtName){
23333         // first do standard event for this node
23334         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23335             return false;
23336         }
23337         // then bubble it up to the tree if the event wasn't cancelled
23338         var ot = this.getOwnerTree();
23339         if(ot){
23340             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23341                 return false;
23342             }
23343         }
23344         return true;
23345     },
23346
23347     /**
23348      * Returns true if this node is a leaf
23349      * @return {Boolean}
23350      */
23351     isLeaf : function(){
23352         return this.leaf === true;
23353     },
23354
23355     // private
23356     setFirstChild : function(node){
23357         this.firstChild = node;
23358     },
23359
23360     //private
23361     setLastChild : function(node){
23362         this.lastChild = node;
23363     },
23364
23365
23366     /**
23367      * Returns true if this node is the last child of its parent
23368      * @return {Boolean}
23369      */
23370     isLast : function(){
23371        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23372     },
23373
23374     /**
23375      * Returns true if this node is the first child of its parent
23376      * @return {Boolean}
23377      */
23378     isFirst : function(){
23379        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23380     },
23381
23382     hasChildNodes : function(){
23383         return !this.isLeaf() && this.childNodes.length > 0;
23384     },
23385
23386     /**
23387      * Insert node(s) as the last child node of this node.
23388      * @param {Node/Array} node The node or Array of nodes to append
23389      * @return {Node} The appended node if single append, or null if an array was passed
23390      */
23391     appendChild : function(node){
23392         var multi = false;
23393         if(node instanceof Array){
23394             multi = node;
23395         }else if(arguments.length > 1){
23396             multi = arguments;
23397         }
23398         // if passed an array or multiple args do them one by one
23399         if(multi){
23400             for(var i = 0, len = multi.length; i < len; i++) {
23401                 this.appendChild(multi[i]);
23402             }
23403         }else{
23404             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23405                 return false;
23406             }
23407             var index = this.childNodes.length;
23408             var oldParent = node.parentNode;
23409             // it's a move, make sure we move it cleanly
23410             if(oldParent){
23411                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23412                     return false;
23413                 }
23414                 oldParent.removeChild(node);
23415             }
23416             index = this.childNodes.length;
23417             if(index == 0){
23418                 this.setFirstChild(node);
23419             }
23420             this.childNodes.push(node);
23421             node.parentNode = this;
23422             var ps = this.childNodes[index-1];
23423             if(ps){
23424                 node.previousSibling = ps;
23425                 ps.nextSibling = node;
23426             }else{
23427                 node.previousSibling = null;
23428             }
23429             node.nextSibling = null;
23430             this.setLastChild(node);
23431             node.setOwnerTree(this.getOwnerTree());
23432             this.fireEvent("append", this.ownerTree, this, node, index);
23433             if(oldParent){
23434                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23435             }
23436             return node;
23437         }
23438     },
23439
23440     /**
23441      * Removes a child node from this node.
23442      * @param {Node} node The node to remove
23443      * @return {Node} The removed node
23444      */
23445     removeChild : function(node){
23446         var index = this.childNodes.indexOf(node);
23447         if(index == -1){
23448             return false;
23449         }
23450         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23451             return false;
23452         }
23453
23454         // remove it from childNodes collection
23455         this.childNodes.splice(index, 1);
23456
23457         // update siblings
23458         if(node.previousSibling){
23459             node.previousSibling.nextSibling = node.nextSibling;
23460         }
23461         if(node.nextSibling){
23462             node.nextSibling.previousSibling = node.previousSibling;
23463         }
23464
23465         // update child refs
23466         if(this.firstChild == node){
23467             this.setFirstChild(node.nextSibling);
23468         }
23469         if(this.lastChild == node){
23470             this.setLastChild(node.previousSibling);
23471         }
23472
23473         node.setOwnerTree(null);
23474         // clear any references from the node
23475         node.parentNode = null;
23476         node.previousSibling = null;
23477         node.nextSibling = null;
23478         this.fireEvent("remove", this.ownerTree, this, node);
23479         return node;
23480     },
23481
23482     /**
23483      * Inserts the first node before the second node in this nodes childNodes collection.
23484      * @param {Node} node The node to insert
23485      * @param {Node} refNode The node to insert before (if null the node is appended)
23486      * @return {Node} The inserted node
23487      */
23488     insertBefore : function(node, refNode){
23489         if(!refNode){ // like standard Dom, refNode can be null for append
23490             return this.appendChild(node);
23491         }
23492         // nothing to do
23493         if(node == refNode){
23494             return false;
23495         }
23496
23497         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23498             return false;
23499         }
23500         var index = this.childNodes.indexOf(refNode);
23501         var oldParent = node.parentNode;
23502         var refIndex = index;
23503
23504         // when moving internally, indexes will change after remove
23505         if(oldParent == this && this.childNodes.indexOf(node) < index){
23506             refIndex--;
23507         }
23508
23509         // it's a move, make sure we move it cleanly
23510         if(oldParent){
23511             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23512                 return false;
23513             }
23514             oldParent.removeChild(node);
23515         }
23516         if(refIndex == 0){
23517             this.setFirstChild(node);
23518         }
23519         this.childNodes.splice(refIndex, 0, node);
23520         node.parentNode = this;
23521         var ps = this.childNodes[refIndex-1];
23522         if(ps){
23523             node.previousSibling = ps;
23524             ps.nextSibling = node;
23525         }else{
23526             node.previousSibling = null;
23527         }
23528         node.nextSibling = refNode;
23529         refNode.previousSibling = node;
23530         node.setOwnerTree(this.getOwnerTree());
23531         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23532         if(oldParent){
23533             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23534         }
23535         return node;
23536     },
23537
23538     /**
23539      * Returns the child node at the specified index.
23540      * @param {Number} index
23541      * @return {Node}
23542      */
23543     item : function(index){
23544         return this.childNodes[index];
23545     },
23546
23547     /**
23548      * Replaces one child node in this node with another.
23549      * @param {Node} newChild The replacement node
23550      * @param {Node} oldChild The node to replace
23551      * @return {Node} The replaced node
23552      */
23553     replaceChild : function(newChild, oldChild){
23554         this.insertBefore(newChild, oldChild);
23555         this.removeChild(oldChild);
23556         return oldChild;
23557     },
23558
23559     /**
23560      * Returns the index of a child node
23561      * @param {Node} node
23562      * @return {Number} The index of the node or -1 if it was not found
23563      */
23564     indexOf : function(child){
23565         return this.childNodes.indexOf(child);
23566     },
23567
23568     /**
23569      * Returns the tree this node is in.
23570      * @return {Tree}
23571      */
23572     getOwnerTree : function(){
23573         // if it doesn't have one, look for one
23574         if(!this.ownerTree){
23575             var p = this;
23576             while(p){
23577                 if(p.ownerTree){
23578                     this.ownerTree = p.ownerTree;
23579                     break;
23580                 }
23581                 p = p.parentNode;
23582             }
23583         }
23584         return this.ownerTree;
23585     },
23586
23587     /**
23588      * Returns depth of this node (the root node has a depth of 0)
23589      * @return {Number}
23590      */
23591     getDepth : function(){
23592         var depth = 0;
23593         var p = this;
23594         while(p.parentNode){
23595             ++depth;
23596             p = p.parentNode;
23597         }
23598         return depth;
23599     },
23600
23601     // private
23602     setOwnerTree : function(tree){
23603         // if it's move, we need to update everyone
23604         if(tree != this.ownerTree){
23605             if(this.ownerTree){
23606                 this.ownerTree.unregisterNode(this);
23607             }
23608             this.ownerTree = tree;
23609             var cs = this.childNodes;
23610             for(var i = 0, len = cs.length; i < len; i++) {
23611                 cs[i].setOwnerTree(tree);
23612             }
23613             if(tree){
23614                 tree.registerNode(this);
23615             }
23616         }
23617     },
23618
23619     /**
23620      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23621      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23622      * @return {String} The path
23623      */
23624     getPath : function(attr){
23625         attr = attr || "id";
23626         var p = this.parentNode;
23627         var b = [this.attributes[attr]];
23628         while(p){
23629             b.unshift(p.attributes[attr]);
23630             p = p.parentNode;
23631         }
23632         var sep = this.getOwnerTree().pathSeparator;
23633         return sep + b.join(sep);
23634     },
23635
23636     /**
23637      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23638      * function call will be the scope provided or the current node. The arguments to the function
23639      * will be the args provided or the current node. If the function returns false at any point,
23640      * the bubble is stopped.
23641      * @param {Function} fn The function to call
23642      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23643      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23644      */
23645     bubble : function(fn, scope, args){
23646         var p = this;
23647         while(p){
23648             if(fn.call(scope || p, args || p) === false){
23649                 break;
23650             }
23651             p = p.parentNode;
23652         }
23653     },
23654
23655     /**
23656      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23657      * function call will be the scope provided or the current node. The arguments to the function
23658      * will be the args provided or the current node. If the function returns false at any point,
23659      * the cascade is stopped on that branch.
23660      * @param {Function} fn The function to call
23661      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23662      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23663      */
23664     cascade : function(fn, scope, args){
23665         if(fn.call(scope || this, args || this) !== false){
23666             var cs = this.childNodes;
23667             for(var i = 0, len = cs.length; i < len; i++) {
23668                 cs[i].cascade(fn, scope, args);
23669             }
23670         }
23671     },
23672
23673     /**
23674      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23675      * function call will be the scope provided or the current node. The arguments to the function
23676      * will be the args provided or the current node. If the function returns false at any point,
23677      * the iteration stops.
23678      * @param {Function} fn The function to call
23679      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23680      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23681      */
23682     eachChild : function(fn, scope, args){
23683         var cs = this.childNodes;
23684         for(var i = 0, len = cs.length; i < len; i++) {
23685                 if(fn.call(scope || this, args || cs[i]) === false){
23686                     break;
23687                 }
23688         }
23689     },
23690
23691     /**
23692      * Finds the first child that has the attribute with the specified value.
23693      * @param {String} attribute The attribute name
23694      * @param {Mixed} value The value to search for
23695      * @return {Node} The found child or null if none was found
23696      */
23697     findChild : function(attribute, value){
23698         var cs = this.childNodes;
23699         for(var i = 0, len = cs.length; i < len; i++) {
23700                 if(cs[i].attributes[attribute] == value){
23701                     return cs[i];
23702                 }
23703         }
23704         return null;
23705     },
23706
23707     /**
23708      * Finds the first child by a custom function. The child matches if the function passed
23709      * returns true.
23710      * @param {Function} fn
23711      * @param {Object} scope (optional)
23712      * @return {Node} The found child or null if none was found
23713      */
23714     findChildBy : function(fn, scope){
23715         var cs = this.childNodes;
23716         for(var i = 0, len = cs.length; i < len; i++) {
23717                 if(fn.call(scope||cs[i], cs[i]) === true){
23718                     return cs[i];
23719                 }
23720         }
23721         return null;
23722     },
23723
23724     /**
23725      * Sorts this nodes children using the supplied sort function
23726      * @param {Function} fn
23727      * @param {Object} scope (optional)
23728      */
23729     sort : function(fn, scope){
23730         var cs = this.childNodes;
23731         var len = cs.length;
23732         if(len > 0){
23733             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23734             cs.sort(sortFn);
23735             for(var i = 0; i < len; i++){
23736                 var n = cs[i];
23737                 n.previousSibling = cs[i-1];
23738                 n.nextSibling = cs[i+1];
23739                 if(i == 0){
23740                     this.setFirstChild(n);
23741                 }
23742                 if(i == len-1){
23743                     this.setLastChild(n);
23744                 }
23745             }
23746         }
23747     },
23748
23749     /**
23750      * Returns true if this node is an ancestor (at any point) of the passed node.
23751      * @param {Node} node
23752      * @return {Boolean}
23753      */
23754     contains : function(node){
23755         return node.isAncestor(this);
23756     },
23757
23758     /**
23759      * Returns true if the passed node is an ancestor (at any point) of this node.
23760      * @param {Node} node
23761      * @return {Boolean}
23762      */
23763     isAncestor : function(node){
23764         var p = this.parentNode;
23765         while(p){
23766             if(p == node){
23767                 return true;
23768             }
23769             p = p.parentNode;
23770         }
23771         return false;
23772     },
23773
23774     toString : function(){
23775         return "[Node"+(this.id?" "+this.id:"")+"]";
23776     }
23777 });/*
23778  * Based on:
23779  * Ext JS Library 1.1.1
23780  * Copyright(c) 2006-2007, Ext JS, LLC.
23781  *
23782  * Originally Released Under LGPL - original licence link has changed is not relivant.
23783  *
23784  * Fork - LGPL
23785  * <script type="text/javascript">
23786  */
23787  (function(){ 
23788 /**
23789  * @class Roo.Layer
23790  * @extends Roo.Element
23791  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23792  * automatic maintaining of shadow/shim positions.
23793  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23794  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23795  * you can pass a string with a CSS class name. False turns off the shadow.
23796  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23797  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23798  * @cfg {String} cls CSS class to add to the element
23799  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23800  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23801  * @constructor
23802  * @param {Object} config An object with config options.
23803  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23804  */
23805
23806 Roo.Layer = function(config, existingEl){
23807     config = config || {};
23808     var dh = Roo.DomHelper;
23809     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23810     if(existingEl){
23811         this.dom = Roo.getDom(existingEl);
23812     }
23813     if(!this.dom){
23814         var o = config.dh || {tag: "div", cls: "x-layer"};
23815         this.dom = dh.append(pel, o);
23816     }
23817     if(config.cls){
23818         this.addClass(config.cls);
23819     }
23820     this.constrain = config.constrain !== false;
23821     this.visibilityMode = Roo.Element.VISIBILITY;
23822     if(config.id){
23823         this.id = this.dom.id = config.id;
23824     }else{
23825         this.id = Roo.id(this.dom);
23826     }
23827     this.zindex = config.zindex || this.getZIndex();
23828     this.position("absolute", this.zindex);
23829     if(config.shadow){
23830         this.shadowOffset = config.shadowOffset || 4;
23831         this.shadow = new Roo.Shadow({
23832             offset : this.shadowOffset,
23833             mode : config.shadow
23834         });
23835     }else{
23836         this.shadowOffset = 0;
23837     }
23838     this.useShim = config.shim !== false && Roo.useShims;
23839     this.useDisplay = config.useDisplay;
23840     this.hide();
23841 };
23842
23843 var supr = Roo.Element.prototype;
23844
23845 // shims are shared among layer to keep from having 100 iframes
23846 var shims = [];
23847
23848 Roo.extend(Roo.Layer, Roo.Element, {
23849
23850     getZIndex : function(){
23851         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23852     },
23853
23854     getShim : function(){
23855         if(!this.useShim){
23856             return null;
23857         }
23858         if(this.shim){
23859             return this.shim;
23860         }
23861         var shim = shims.shift();
23862         if(!shim){
23863             shim = this.createShim();
23864             shim.enableDisplayMode('block');
23865             shim.dom.style.display = 'none';
23866             shim.dom.style.visibility = 'visible';
23867         }
23868         var pn = this.dom.parentNode;
23869         if(shim.dom.parentNode != pn){
23870             pn.insertBefore(shim.dom, this.dom);
23871         }
23872         shim.setStyle('z-index', this.getZIndex()-2);
23873         this.shim = shim;
23874         return shim;
23875     },
23876
23877     hideShim : function(){
23878         if(this.shim){
23879             this.shim.setDisplayed(false);
23880             shims.push(this.shim);
23881             delete this.shim;
23882         }
23883     },
23884
23885     disableShadow : function(){
23886         if(this.shadow){
23887             this.shadowDisabled = true;
23888             this.shadow.hide();
23889             this.lastShadowOffset = this.shadowOffset;
23890             this.shadowOffset = 0;
23891         }
23892     },
23893
23894     enableShadow : function(show){
23895         if(this.shadow){
23896             this.shadowDisabled = false;
23897             this.shadowOffset = this.lastShadowOffset;
23898             delete this.lastShadowOffset;
23899             if(show){
23900                 this.sync(true);
23901             }
23902         }
23903     },
23904
23905     // private
23906     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23907     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23908     sync : function(doShow){
23909         var sw = this.shadow;
23910         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23911             var sh = this.getShim();
23912
23913             var w = this.getWidth(),
23914                 h = this.getHeight();
23915
23916             var l = this.getLeft(true),
23917                 t = this.getTop(true);
23918
23919             if(sw && !this.shadowDisabled){
23920                 if(doShow && !sw.isVisible()){
23921                     sw.show(this);
23922                 }else{
23923                     sw.realign(l, t, w, h);
23924                 }
23925                 if(sh){
23926                     if(doShow){
23927                        sh.show();
23928                     }
23929                     // fit the shim behind the shadow, so it is shimmed too
23930                     var a = sw.adjusts, s = sh.dom.style;
23931                     s.left = (Math.min(l, l+a.l))+"px";
23932                     s.top = (Math.min(t, t+a.t))+"px";
23933                     s.width = (w+a.w)+"px";
23934                     s.height = (h+a.h)+"px";
23935                 }
23936             }else if(sh){
23937                 if(doShow){
23938                    sh.show();
23939                 }
23940                 sh.setSize(w, h);
23941                 sh.setLeftTop(l, t);
23942             }
23943             
23944         }
23945     },
23946
23947     // private
23948     destroy : function(){
23949         this.hideShim();
23950         if(this.shadow){
23951             this.shadow.hide();
23952         }
23953         this.removeAllListeners();
23954         var pn = this.dom.parentNode;
23955         if(pn){
23956             pn.removeChild(this.dom);
23957         }
23958         Roo.Element.uncache(this.id);
23959     },
23960
23961     remove : function(){
23962         this.destroy();
23963     },
23964
23965     // private
23966     beginUpdate : function(){
23967         this.updating = true;
23968     },
23969
23970     // private
23971     endUpdate : function(){
23972         this.updating = false;
23973         this.sync(true);
23974     },
23975
23976     // private
23977     hideUnders : function(negOffset){
23978         if(this.shadow){
23979             this.shadow.hide();
23980         }
23981         this.hideShim();
23982     },
23983
23984     // private
23985     constrainXY : function(){
23986         if(this.constrain){
23987             var vw = Roo.lib.Dom.getViewWidth(),
23988                 vh = Roo.lib.Dom.getViewHeight();
23989             var s = Roo.get(document).getScroll();
23990
23991             var xy = this.getXY();
23992             var x = xy[0], y = xy[1];   
23993             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23994             // only move it if it needs it
23995             var moved = false;
23996             // first validate right/bottom
23997             if((x + w) > vw+s.left){
23998                 x = vw - w - this.shadowOffset;
23999                 moved = true;
24000             }
24001             if((y + h) > vh+s.top){
24002                 y = vh - h - this.shadowOffset;
24003                 moved = true;
24004             }
24005             // then make sure top/left isn't negative
24006             if(x < s.left){
24007                 x = s.left;
24008                 moved = true;
24009             }
24010             if(y < s.top){
24011                 y = s.top;
24012                 moved = true;
24013             }
24014             if(moved){
24015                 if(this.avoidY){
24016                     var ay = this.avoidY;
24017                     if(y <= ay && (y+h) >= ay){
24018                         y = ay-h-5;   
24019                     }
24020                 }
24021                 xy = [x, y];
24022                 this.storeXY(xy);
24023                 supr.setXY.call(this, xy);
24024                 this.sync();
24025             }
24026         }
24027     },
24028
24029     isVisible : function(){
24030         return this.visible;    
24031     },
24032
24033     // private
24034     showAction : function(){
24035         this.visible = true; // track visibility to prevent getStyle calls
24036         if(this.useDisplay === true){
24037             this.setDisplayed("");
24038         }else if(this.lastXY){
24039             supr.setXY.call(this, this.lastXY);
24040         }else if(this.lastLT){
24041             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24042         }
24043     },
24044
24045     // private
24046     hideAction : function(){
24047         this.visible = false;
24048         if(this.useDisplay === true){
24049             this.setDisplayed(false);
24050         }else{
24051             this.setLeftTop(-10000,-10000);
24052         }
24053     },
24054
24055     // overridden Element method
24056     setVisible : function(v, a, d, c, e){
24057         if(v){
24058             this.showAction();
24059         }
24060         if(a && v){
24061             var cb = function(){
24062                 this.sync(true);
24063                 if(c){
24064                     c();
24065                 }
24066             }.createDelegate(this);
24067             supr.setVisible.call(this, true, true, d, cb, e);
24068         }else{
24069             if(!v){
24070                 this.hideUnders(true);
24071             }
24072             var cb = c;
24073             if(a){
24074                 cb = function(){
24075                     this.hideAction();
24076                     if(c){
24077                         c();
24078                     }
24079                 }.createDelegate(this);
24080             }
24081             supr.setVisible.call(this, v, a, d, cb, e);
24082             if(v){
24083                 this.sync(true);
24084             }else if(!a){
24085                 this.hideAction();
24086             }
24087         }
24088     },
24089
24090     storeXY : function(xy){
24091         delete this.lastLT;
24092         this.lastXY = xy;
24093     },
24094
24095     storeLeftTop : function(left, top){
24096         delete this.lastXY;
24097         this.lastLT = [left, top];
24098     },
24099
24100     // private
24101     beforeFx : function(){
24102         this.beforeAction();
24103         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24104     },
24105
24106     // private
24107     afterFx : function(){
24108         Roo.Layer.superclass.afterFx.apply(this, arguments);
24109         this.sync(this.isVisible());
24110     },
24111
24112     // private
24113     beforeAction : function(){
24114         if(!this.updating && this.shadow){
24115             this.shadow.hide();
24116         }
24117     },
24118
24119     // overridden Element method
24120     setLeft : function(left){
24121         this.storeLeftTop(left, this.getTop(true));
24122         supr.setLeft.apply(this, arguments);
24123         this.sync();
24124     },
24125
24126     setTop : function(top){
24127         this.storeLeftTop(this.getLeft(true), top);
24128         supr.setTop.apply(this, arguments);
24129         this.sync();
24130     },
24131
24132     setLeftTop : function(left, top){
24133         this.storeLeftTop(left, top);
24134         supr.setLeftTop.apply(this, arguments);
24135         this.sync();
24136     },
24137
24138     setXY : function(xy, a, d, c, e){
24139         this.fixDisplay();
24140         this.beforeAction();
24141         this.storeXY(xy);
24142         var cb = this.createCB(c);
24143         supr.setXY.call(this, xy, a, d, cb, e);
24144         if(!a){
24145             cb();
24146         }
24147     },
24148
24149     // private
24150     createCB : function(c){
24151         var el = this;
24152         return function(){
24153             el.constrainXY();
24154             el.sync(true);
24155             if(c){
24156                 c();
24157             }
24158         };
24159     },
24160
24161     // overridden Element method
24162     setX : function(x, a, d, c, e){
24163         this.setXY([x, this.getY()], a, d, c, e);
24164     },
24165
24166     // overridden Element method
24167     setY : function(y, a, d, c, e){
24168         this.setXY([this.getX(), y], a, d, c, e);
24169     },
24170
24171     // overridden Element method
24172     setSize : function(w, h, a, d, c, e){
24173         this.beforeAction();
24174         var cb = this.createCB(c);
24175         supr.setSize.call(this, w, h, a, d, cb, e);
24176         if(!a){
24177             cb();
24178         }
24179     },
24180
24181     // overridden Element method
24182     setWidth : function(w, a, d, c, e){
24183         this.beforeAction();
24184         var cb = this.createCB(c);
24185         supr.setWidth.call(this, w, a, d, cb, e);
24186         if(!a){
24187             cb();
24188         }
24189     },
24190
24191     // overridden Element method
24192     setHeight : function(h, a, d, c, e){
24193         this.beforeAction();
24194         var cb = this.createCB(c);
24195         supr.setHeight.call(this, h, a, d, cb, e);
24196         if(!a){
24197             cb();
24198         }
24199     },
24200
24201     // overridden Element method
24202     setBounds : function(x, y, w, h, a, d, c, e){
24203         this.beforeAction();
24204         var cb = this.createCB(c);
24205         if(!a){
24206             this.storeXY([x, y]);
24207             supr.setXY.call(this, [x, y]);
24208             supr.setSize.call(this, w, h, a, d, cb, e);
24209             cb();
24210         }else{
24211             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24212         }
24213         return this;
24214     },
24215     
24216     /**
24217      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24218      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24219      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24220      * @param {Number} zindex The new z-index to set
24221      * @return {this} The Layer
24222      */
24223     setZIndex : function(zindex){
24224         this.zindex = zindex;
24225         this.setStyle("z-index", zindex + 2);
24226         if(this.shadow){
24227             this.shadow.setZIndex(zindex + 1);
24228         }
24229         if(this.shim){
24230             this.shim.setStyle("z-index", zindex);
24231         }
24232     }
24233 });
24234 })();/*
24235  * Based on:
24236  * Ext JS Library 1.1.1
24237  * Copyright(c) 2006-2007, Ext JS, LLC.
24238  *
24239  * Originally Released Under LGPL - original licence link has changed is not relivant.
24240  *
24241  * Fork - LGPL
24242  * <script type="text/javascript">
24243  */
24244
24245
24246 /**
24247  * @class Roo.Shadow
24248  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24249  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24250  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24251  * @constructor
24252  * Create a new Shadow
24253  * @param {Object} config The config object
24254  */
24255 Roo.Shadow = function(config){
24256     Roo.apply(this, config);
24257     if(typeof this.mode != "string"){
24258         this.mode = this.defaultMode;
24259     }
24260     var o = this.offset, a = {h: 0};
24261     var rad = Math.floor(this.offset/2);
24262     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24263         case "drop":
24264             a.w = 0;
24265             a.l = a.t = o;
24266             a.t -= 1;
24267             if(Roo.isIE){
24268                 a.l -= this.offset + rad;
24269                 a.t -= this.offset + rad;
24270                 a.w -= rad;
24271                 a.h -= rad;
24272                 a.t += 1;
24273             }
24274         break;
24275         case "sides":
24276             a.w = (o*2);
24277             a.l = -o;
24278             a.t = o-1;
24279             if(Roo.isIE){
24280                 a.l -= (this.offset - rad);
24281                 a.t -= this.offset + rad;
24282                 a.l += 1;
24283                 a.w -= (this.offset - rad)*2;
24284                 a.w -= rad + 1;
24285                 a.h -= 1;
24286             }
24287         break;
24288         case "frame":
24289             a.w = a.h = (o*2);
24290             a.l = a.t = -o;
24291             a.t += 1;
24292             a.h -= 2;
24293             if(Roo.isIE){
24294                 a.l -= (this.offset - rad);
24295                 a.t -= (this.offset - rad);
24296                 a.l += 1;
24297                 a.w -= (this.offset + rad + 1);
24298                 a.h -= (this.offset + rad);
24299                 a.h += 1;
24300             }
24301         break;
24302     };
24303
24304     this.adjusts = a;
24305 };
24306
24307 Roo.Shadow.prototype = {
24308     /**
24309      * @cfg {String} mode
24310      * The shadow display mode.  Supports the following options:<br />
24311      * sides: Shadow displays on both sides and bottom only<br />
24312      * frame: Shadow displays equally on all four sides<br />
24313      * drop: Traditional bottom-right drop shadow (default)
24314      */
24315     /**
24316      * @cfg {String} offset
24317      * The number of pixels to offset the shadow from the element (defaults to 4)
24318      */
24319     offset: 4,
24320
24321     // private
24322     defaultMode: "drop",
24323
24324     /**
24325      * Displays the shadow under the target element
24326      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24327      */
24328     show : function(target){
24329         target = Roo.get(target);
24330         if(!this.el){
24331             this.el = Roo.Shadow.Pool.pull();
24332             if(this.el.dom.nextSibling != target.dom){
24333                 this.el.insertBefore(target);
24334             }
24335         }
24336         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24337         if(Roo.isIE){
24338             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24339         }
24340         this.realign(
24341             target.getLeft(true),
24342             target.getTop(true),
24343             target.getWidth(),
24344             target.getHeight()
24345         );
24346         this.el.dom.style.display = "block";
24347     },
24348
24349     /**
24350      * Returns true if the shadow is visible, else false
24351      */
24352     isVisible : function(){
24353         return this.el ? true : false;  
24354     },
24355
24356     /**
24357      * Direct alignment when values are already available. Show must be called at least once before
24358      * calling this method to ensure it is initialized.
24359      * @param {Number} left The target element left position
24360      * @param {Number} top The target element top position
24361      * @param {Number} width The target element width
24362      * @param {Number} height The target element height
24363      */
24364     realign : function(l, t, w, h){
24365         if(!this.el){
24366             return;
24367         }
24368         var a = this.adjusts, d = this.el.dom, s = d.style;
24369         var iea = 0;
24370         s.left = (l+a.l)+"px";
24371         s.top = (t+a.t)+"px";
24372         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24373  
24374         if(s.width != sws || s.height != shs){
24375             s.width = sws;
24376             s.height = shs;
24377             if(!Roo.isIE){
24378                 var cn = d.childNodes;
24379                 var sww = Math.max(0, (sw-12))+"px";
24380                 cn[0].childNodes[1].style.width = sww;
24381                 cn[1].childNodes[1].style.width = sww;
24382                 cn[2].childNodes[1].style.width = sww;
24383                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24384             }
24385         }
24386     },
24387
24388     /**
24389      * Hides this shadow
24390      */
24391     hide : function(){
24392         if(this.el){
24393             this.el.dom.style.display = "none";
24394             Roo.Shadow.Pool.push(this.el);
24395             delete this.el;
24396         }
24397     },
24398
24399     /**
24400      * Adjust the z-index of this shadow
24401      * @param {Number} zindex The new z-index
24402      */
24403     setZIndex : function(z){
24404         this.zIndex = z;
24405         if(this.el){
24406             this.el.setStyle("z-index", z);
24407         }
24408     }
24409 };
24410
24411 // Private utility class that manages the internal Shadow cache
24412 Roo.Shadow.Pool = function(){
24413     var p = [];
24414     var markup = Roo.isIE ?
24415                  '<div class="x-ie-shadow"></div>' :
24416                  '<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>';
24417     return {
24418         pull : function(){
24419             var sh = p.shift();
24420             if(!sh){
24421                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24422                 sh.autoBoxAdjust = false;
24423             }
24424             return sh;
24425         },
24426
24427         push : function(sh){
24428             p.push(sh);
24429         }
24430     };
24431 }();/*
24432  * Based on:
24433  * Ext JS Library 1.1.1
24434  * Copyright(c) 2006-2007, Ext JS, LLC.
24435  *
24436  * Originally Released Under LGPL - original licence link has changed is not relivant.
24437  *
24438  * Fork - LGPL
24439  * <script type="text/javascript">
24440  */
24441
24442
24443 /**
24444  * @class Roo.SplitBar
24445  * @extends Roo.util.Observable
24446  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24447  * <br><br>
24448  * Usage:
24449  * <pre><code>
24450 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24451                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24452 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24453 split.minSize = 100;
24454 split.maxSize = 600;
24455 split.animate = true;
24456 split.on('moved', splitterMoved);
24457 </code></pre>
24458  * @constructor
24459  * Create a new SplitBar
24460  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24461  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24462  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24463  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24464                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24465                         position of the SplitBar).
24466  */
24467 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24468     
24469     /** @private */
24470     this.el = Roo.get(dragElement, true);
24471     this.el.dom.unselectable = "on";
24472     /** @private */
24473     this.resizingEl = Roo.get(resizingElement, true);
24474
24475     /**
24476      * @private
24477      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24478      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24479      * @type Number
24480      */
24481     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24482     
24483     /**
24484      * The minimum size of the resizing element. (Defaults to 0)
24485      * @type Number
24486      */
24487     this.minSize = 0;
24488     
24489     /**
24490      * The maximum size of the resizing element. (Defaults to 2000)
24491      * @type Number
24492      */
24493     this.maxSize = 2000;
24494     
24495     /**
24496      * Whether to animate the transition to the new size
24497      * @type Boolean
24498      */
24499     this.animate = false;
24500     
24501     /**
24502      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24503      * @type Boolean
24504      */
24505     this.useShim = false;
24506     
24507     /** @private */
24508     this.shim = null;
24509     
24510     if(!existingProxy){
24511         /** @private */
24512         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24513     }else{
24514         this.proxy = Roo.get(existingProxy).dom;
24515     }
24516     /** @private */
24517     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24518     
24519     /** @private */
24520     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24521     
24522     /** @private */
24523     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24524     
24525     /** @private */
24526     this.dragSpecs = {};
24527     
24528     /**
24529      * @private The adapter to use to positon and resize elements
24530      */
24531     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24532     this.adapter.init(this);
24533     
24534     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24535         /** @private */
24536         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24537         this.el.addClass("x-splitbar-h");
24538     }else{
24539         /** @private */
24540         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24541         this.el.addClass("x-splitbar-v");
24542     }
24543     
24544     this.addEvents({
24545         /**
24546          * @event resize
24547          * Fires when the splitter is moved (alias for {@link #event-moved})
24548          * @param {Roo.SplitBar} this
24549          * @param {Number} newSize the new width or height
24550          */
24551         "resize" : true,
24552         /**
24553          * @event moved
24554          * Fires when the splitter is moved
24555          * @param {Roo.SplitBar} this
24556          * @param {Number} newSize the new width or height
24557          */
24558         "moved" : true,
24559         /**
24560          * @event beforeresize
24561          * Fires before the splitter is dragged
24562          * @param {Roo.SplitBar} this
24563          */
24564         "beforeresize" : true,
24565
24566         "beforeapply" : true
24567     });
24568
24569     Roo.util.Observable.call(this);
24570 };
24571
24572 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24573     onStartProxyDrag : function(x, y){
24574         this.fireEvent("beforeresize", this);
24575         if(!this.overlay){
24576             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24577             o.unselectable();
24578             o.enableDisplayMode("block");
24579             // all splitbars share the same overlay
24580             Roo.SplitBar.prototype.overlay = o;
24581         }
24582         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24583         this.overlay.show();
24584         Roo.get(this.proxy).setDisplayed("block");
24585         var size = this.adapter.getElementSize(this);
24586         this.activeMinSize = this.getMinimumSize();;
24587         this.activeMaxSize = this.getMaximumSize();;
24588         var c1 = size - this.activeMinSize;
24589         var c2 = Math.max(this.activeMaxSize - size, 0);
24590         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24591             this.dd.resetConstraints();
24592             this.dd.setXConstraint(
24593                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24594                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24595             );
24596             this.dd.setYConstraint(0, 0);
24597         }else{
24598             this.dd.resetConstraints();
24599             this.dd.setXConstraint(0, 0);
24600             this.dd.setYConstraint(
24601                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24602                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24603             );
24604          }
24605         this.dragSpecs.startSize = size;
24606         this.dragSpecs.startPoint = [x, y];
24607         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24608     },
24609     
24610     /** 
24611      * @private Called after the drag operation by the DDProxy
24612      */
24613     onEndProxyDrag : function(e){
24614         Roo.get(this.proxy).setDisplayed(false);
24615         var endPoint = Roo.lib.Event.getXY(e);
24616         if(this.overlay){
24617             this.overlay.hide();
24618         }
24619         var newSize;
24620         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24621             newSize = this.dragSpecs.startSize + 
24622                 (this.placement == Roo.SplitBar.LEFT ?
24623                     endPoint[0] - this.dragSpecs.startPoint[0] :
24624                     this.dragSpecs.startPoint[0] - endPoint[0]
24625                 );
24626         }else{
24627             newSize = this.dragSpecs.startSize + 
24628                 (this.placement == Roo.SplitBar.TOP ?
24629                     endPoint[1] - this.dragSpecs.startPoint[1] :
24630                     this.dragSpecs.startPoint[1] - endPoint[1]
24631                 );
24632         }
24633         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24634         if(newSize != this.dragSpecs.startSize){
24635             if(this.fireEvent('beforeapply', this, newSize) !== false){
24636                 this.adapter.setElementSize(this, newSize);
24637                 this.fireEvent("moved", this, newSize);
24638                 this.fireEvent("resize", this, newSize);
24639             }
24640         }
24641     },
24642     
24643     /**
24644      * Get the adapter this SplitBar uses
24645      * @return The adapter object
24646      */
24647     getAdapter : function(){
24648         return this.adapter;
24649     },
24650     
24651     /**
24652      * Set the adapter this SplitBar uses
24653      * @param {Object} adapter A SplitBar adapter object
24654      */
24655     setAdapter : function(adapter){
24656         this.adapter = adapter;
24657         this.adapter.init(this);
24658     },
24659     
24660     /**
24661      * Gets the minimum size for the resizing element
24662      * @return {Number} The minimum size
24663      */
24664     getMinimumSize : function(){
24665         return this.minSize;
24666     },
24667     
24668     /**
24669      * Sets the minimum size for the resizing element
24670      * @param {Number} minSize The minimum size
24671      */
24672     setMinimumSize : function(minSize){
24673         this.minSize = minSize;
24674     },
24675     
24676     /**
24677      * Gets the maximum size for the resizing element
24678      * @return {Number} The maximum size
24679      */
24680     getMaximumSize : function(){
24681         return this.maxSize;
24682     },
24683     
24684     /**
24685      * Sets the maximum size for the resizing element
24686      * @param {Number} maxSize The maximum size
24687      */
24688     setMaximumSize : function(maxSize){
24689         this.maxSize = maxSize;
24690     },
24691     
24692     /**
24693      * Sets the initialize size for the resizing element
24694      * @param {Number} size The initial size
24695      */
24696     setCurrentSize : function(size){
24697         var oldAnimate = this.animate;
24698         this.animate = false;
24699         this.adapter.setElementSize(this, size);
24700         this.animate = oldAnimate;
24701     },
24702     
24703     /**
24704      * Destroy this splitbar. 
24705      * @param {Boolean} removeEl True to remove the element
24706      */
24707     destroy : function(removeEl){
24708         if(this.shim){
24709             this.shim.remove();
24710         }
24711         this.dd.unreg();
24712         this.proxy.parentNode.removeChild(this.proxy);
24713         if(removeEl){
24714             this.el.remove();
24715         }
24716     }
24717 });
24718
24719 /**
24720  * @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.
24721  */
24722 Roo.SplitBar.createProxy = function(dir){
24723     var proxy = new Roo.Element(document.createElement("div"));
24724     proxy.unselectable();
24725     var cls = 'x-splitbar-proxy';
24726     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24727     document.body.appendChild(proxy.dom);
24728     return proxy.dom;
24729 };
24730
24731 /** 
24732  * @class Roo.SplitBar.BasicLayoutAdapter
24733  * Default Adapter. It assumes the splitter and resizing element are not positioned
24734  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24735  */
24736 Roo.SplitBar.BasicLayoutAdapter = function(){
24737 };
24738
24739 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24740     // do nothing for now
24741     init : function(s){
24742     
24743     },
24744     /**
24745      * Called before drag operations to get the current size of the resizing element. 
24746      * @param {Roo.SplitBar} s The SplitBar using this adapter
24747      */
24748      getElementSize : function(s){
24749         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24750             return s.resizingEl.getWidth();
24751         }else{
24752             return s.resizingEl.getHeight();
24753         }
24754     },
24755     
24756     /**
24757      * Called after drag operations to set the size of the resizing element.
24758      * @param {Roo.SplitBar} s The SplitBar using this adapter
24759      * @param {Number} newSize The new size to set
24760      * @param {Function} onComplete A function to be invoked when resizing is complete
24761      */
24762     setElementSize : function(s, newSize, onComplete){
24763         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24764             if(!s.animate){
24765                 s.resizingEl.setWidth(newSize);
24766                 if(onComplete){
24767                     onComplete(s, newSize);
24768                 }
24769             }else{
24770                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24771             }
24772         }else{
24773             
24774             if(!s.animate){
24775                 s.resizingEl.setHeight(newSize);
24776                 if(onComplete){
24777                     onComplete(s, newSize);
24778                 }
24779             }else{
24780                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24781             }
24782         }
24783     }
24784 };
24785
24786 /** 
24787  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24788  * @extends Roo.SplitBar.BasicLayoutAdapter
24789  * Adapter that  moves the splitter element to align with the resized sizing element. 
24790  * Used with an absolute positioned SplitBar.
24791  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24792  * document.body, make sure you assign an id to the body element.
24793  */
24794 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24795     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24796     this.container = Roo.get(container);
24797 };
24798
24799 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24800     init : function(s){
24801         this.basic.init(s);
24802     },
24803     
24804     getElementSize : function(s){
24805         return this.basic.getElementSize(s);
24806     },
24807     
24808     setElementSize : function(s, newSize, onComplete){
24809         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24810     },
24811     
24812     moveSplitter : function(s){
24813         var yes = Roo.SplitBar;
24814         switch(s.placement){
24815             case yes.LEFT:
24816                 s.el.setX(s.resizingEl.getRight());
24817                 break;
24818             case yes.RIGHT:
24819                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24820                 break;
24821             case yes.TOP:
24822                 s.el.setY(s.resizingEl.getBottom());
24823                 break;
24824             case yes.BOTTOM:
24825                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24826                 break;
24827         }
24828     }
24829 };
24830
24831 /**
24832  * Orientation constant - Create a vertical SplitBar
24833  * @static
24834  * @type Number
24835  */
24836 Roo.SplitBar.VERTICAL = 1;
24837
24838 /**
24839  * Orientation constant - Create a horizontal SplitBar
24840  * @static
24841  * @type Number
24842  */
24843 Roo.SplitBar.HORIZONTAL = 2;
24844
24845 /**
24846  * Placement constant - The resizing element is to the left of the splitter element
24847  * @static
24848  * @type Number
24849  */
24850 Roo.SplitBar.LEFT = 1;
24851
24852 /**
24853  * Placement constant - The resizing element is to the right of the splitter element
24854  * @static
24855  * @type Number
24856  */
24857 Roo.SplitBar.RIGHT = 2;
24858
24859 /**
24860  * Placement constant - The resizing element is positioned above the splitter element
24861  * @static
24862  * @type Number
24863  */
24864 Roo.SplitBar.TOP = 3;
24865
24866 /**
24867  * Placement constant - The resizing element is positioned under splitter element
24868  * @static
24869  * @type Number
24870  */
24871 Roo.SplitBar.BOTTOM = 4;
24872 /*
24873  * Based on:
24874  * Ext JS Library 1.1.1
24875  * Copyright(c) 2006-2007, Ext JS, LLC.
24876  *
24877  * Originally Released Under LGPL - original licence link has changed is not relivant.
24878  *
24879  * Fork - LGPL
24880  * <script type="text/javascript">
24881  */
24882
24883 /**
24884  * @class Roo.View
24885  * @extends Roo.util.Observable
24886  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24887  * This class also supports single and multi selection modes. <br>
24888  * Create a data model bound view:
24889  <pre><code>
24890  var store = new Roo.data.Store(...);
24891
24892  var view = new Roo.View({
24893     el : "my-element",
24894     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24895  
24896     singleSelect: true,
24897     selectedClass: "ydataview-selected",
24898     store: store
24899  });
24900
24901  // listen for node click?
24902  view.on("click", function(vw, index, node, e){
24903  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24904  });
24905
24906  // load XML data
24907  dataModel.load("foobar.xml");
24908  </code></pre>
24909  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24910  * <br><br>
24911  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24912  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24913  * 
24914  * Note: old style constructor is still suported (container, template, config)
24915  * 
24916  * @constructor
24917  * Create a new View
24918  * @param {Object} config The config object
24919  * 
24920  */
24921 Roo.View = function(config, depreciated_tpl, depreciated_config){
24922     
24923     this.parent = false;
24924     
24925     if (typeof(depreciated_tpl) == 'undefined') {
24926         // new way.. - universal constructor.
24927         Roo.apply(this, config);
24928         this.el  = Roo.get(this.el);
24929     } else {
24930         // old format..
24931         this.el  = Roo.get(config);
24932         this.tpl = depreciated_tpl;
24933         Roo.apply(this, depreciated_config);
24934     }
24935     this.wrapEl  = this.el.wrap().wrap();
24936     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24937     
24938     
24939     if(typeof(this.tpl) == "string"){
24940         this.tpl = new Roo.Template(this.tpl);
24941     } else {
24942         // support xtype ctors..
24943         this.tpl = new Roo.factory(this.tpl, Roo);
24944     }
24945     
24946     
24947     this.tpl.compile();
24948     
24949     /** @private */
24950     this.addEvents({
24951         /**
24952          * @event beforeclick
24953          * Fires before a click is processed. Returns false to cancel the default action.
24954          * @param {Roo.View} this
24955          * @param {Number} index The index of the target node
24956          * @param {HTMLElement} node The target node
24957          * @param {Roo.EventObject} e The raw event object
24958          */
24959             "beforeclick" : true,
24960         /**
24961          * @event click
24962          * Fires when a template node is clicked.
24963          * @param {Roo.View} this
24964          * @param {Number} index The index of the target node
24965          * @param {HTMLElement} node The target node
24966          * @param {Roo.EventObject} e The raw event object
24967          */
24968             "click" : true,
24969         /**
24970          * @event dblclick
24971          * Fires when a template node is double clicked.
24972          * @param {Roo.View} this
24973          * @param {Number} index The index of the target node
24974          * @param {HTMLElement} node The target node
24975          * @param {Roo.EventObject} e The raw event object
24976          */
24977             "dblclick" : true,
24978         /**
24979          * @event contextmenu
24980          * Fires when a template node is right clicked.
24981          * @param {Roo.View} this
24982          * @param {Number} index The index of the target node
24983          * @param {HTMLElement} node The target node
24984          * @param {Roo.EventObject} e The raw event object
24985          */
24986             "contextmenu" : true,
24987         /**
24988          * @event selectionchange
24989          * Fires when the selected nodes change.
24990          * @param {Roo.View} this
24991          * @param {Array} selections Array of the selected nodes
24992          */
24993             "selectionchange" : true,
24994     
24995         /**
24996          * @event beforeselect
24997          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24998          * @param {Roo.View} this
24999          * @param {HTMLElement} node The node to be selected
25000          * @param {Array} selections Array of currently selected nodes
25001          */
25002             "beforeselect" : true,
25003         /**
25004          * @event preparedata
25005          * Fires on every row to render, to allow you to change the data.
25006          * @param {Roo.View} this
25007          * @param {Object} data to be rendered (change this)
25008          */
25009           "preparedata" : true
25010           
25011           
25012         });
25013
25014
25015
25016     this.el.on({
25017         "click": this.onClick,
25018         "dblclick": this.onDblClick,
25019         "contextmenu": this.onContextMenu,
25020         scope:this
25021     });
25022
25023     this.selections = [];
25024     this.nodes = [];
25025     this.cmp = new Roo.CompositeElementLite([]);
25026     if(this.store){
25027         this.store = Roo.factory(this.store, Roo.data);
25028         this.setStore(this.store, true);
25029     }
25030     
25031     if ( this.footer && this.footer.xtype) {
25032            
25033          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25034         
25035         this.footer.dataSource = this.store
25036         this.footer.container = fctr;
25037         this.footer = Roo.factory(this.footer, Roo);
25038         fctr.insertFirst(this.el);
25039         
25040         // this is a bit insane - as the paging toolbar seems to detach the el..
25041 //        dom.parentNode.parentNode.parentNode
25042          // they get detached?
25043     }
25044     
25045     
25046     Roo.View.superclass.constructor.call(this);
25047     
25048     
25049 };
25050
25051 Roo.extend(Roo.View, Roo.util.Observable, {
25052     
25053      /**
25054      * @cfg {Roo.data.Store} store Data store to load data from.
25055      */
25056     store : false,
25057     
25058     /**
25059      * @cfg {String|Roo.Element} el The container element.
25060      */
25061     el : '',
25062     
25063     /**
25064      * @cfg {String|Roo.Template} tpl The template used by this View 
25065      */
25066     tpl : false,
25067     /**
25068      * @cfg {String} dataName the named area of the template to use as the data area
25069      *                          Works with domtemplates roo-name="name"
25070      */
25071     dataName: false,
25072     /**
25073      * @cfg {String} selectedClass The css class to add to selected nodes
25074      */
25075     selectedClass : "x-view-selected",
25076      /**
25077      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25078      */
25079     emptyText : "",
25080     
25081     /**
25082      * @cfg {String} text to display on mask (default Loading)
25083      */
25084     mask : false,
25085     /**
25086      * @cfg {Boolean} multiSelect Allow multiple selection
25087      */
25088     multiSelect : false,
25089     /**
25090      * @cfg {Boolean} singleSelect Allow single selection
25091      */
25092     singleSelect:  false,
25093     
25094     /**
25095      * @cfg {Boolean} toggleSelect - selecting 
25096      */
25097     toggleSelect : false,
25098     
25099     /**
25100      * @cfg {Boolean} tickable - selecting 
25101      */
25102     tickable : false,
25103     
25104     /**
25105      * Returns the element this view is bound to.
25106      * @return {Roo.Element}
25107      */
25108     getEl : function(){
25109         return this.wrapEl;
25110     },
25111     
25112     
25113
25114     /**
25115      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25116      */
25117     refresh : function(){
25118         //Roo.log('refresh');
25119         var t = this.tpl;
25120         
25121         // if we are using something like 'domtemplate', then
25122         // the what gets used is:
25123         // t.applySubtemplate(NAME, data, wrapping data..)
25124         // the outer template then get' applied with
25125         //     the store 'extra data'
25126         // and the body get's added to the
25127         //      roo-name="data" node?
25128         //      <span class='roo-tpl-{name}'></span> ?????
25129         
25130         
25131         
25132         this.clearSelections();
25133         this.el.update("");
25134         var html = [];
25135         var records = this.store.getRange();
25136         if(records.length < 1) {
25137             
25138             // is this valid??  = should it render a template??
25139             
25140             this.el.update(this.emptyText);
25141             return;
25142         }
25143         var el = this.el;
25144         if (this.dataName) {
25145             this.el.update(t.apply(this.store.meta)); //????
25146             el = this.el.child('.roo-tpl-' + this.dataName);
25147         }
25148         
25149         for(var i = 0, len = records.length; i < len; i++){
25150             var data = this.prepareData(records[i].data, i, records[i]);
25151             this.fireEvent("preparedata", this, data, i, records[i]);
25152             
25153             var d = Roo.apply({}, data);
25154             
25155             if(this.tickable){
25156                 Roo.apply(d, {'roo-id' : Roo.id()});
25157                 
25158                 var _this = this;
25159             
25160                 Roo.each(this.parent.item, function(item){
25161                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25162                         return;
25163                     }
25164                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25165                 });
25166             }
25167             
25168             html[html.length] = Roo.util.Format.trim(
25169                 this.dataName ?
25170                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25171                     t.apply(d)
25172             );
25173         }
25174         
25175         
25176         
25177         el.update(html.join(""));
25178         this.nodes = el.dom.childNodes;
25179         this.updateIndexes(0);
25180     },
25181     
25182
25183     /**
25184      * Function to override to reformat the data that is sent to
25185      * the template for each node.
25186      * DEPRICATED - use the preparedata event handler.
25187      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25188      * a JSON object for an UpdateManager bound view).
25189      */
25190     prepareData : function(data, index, record)
25191     {
25192         this.fireEvent("preparedata", this, data, index, record);
25193         return data;
25194     },
25195
25196     onUpdate : function(ds, record){
25197         // Roo.log('on update');   
25198         this.clearSelections();
25199         var index = this.store.indexOf(record);
25200         var n = this.nodes[index];
25201         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25202         n.parentNode.removeChild(n);
25203         this.updateIndexes(index, index);
25204     },
25205
25206     
25207     
25208 // --------- FIXME     
25209     onAdd : function(ds, records, index)
25210     {
25211         //Roo.log(['on Add', ds, records, index] );        
25212         this.clearSelections();
25213         if(this.nodes.length == 0){
25214             this.refresh();
25215             return;
25216         }
25217         var n = this.nodes[index];
25218         for(var i = 0, len = records.length; i < len; i++){
25219             var d = this.prepareData(records[i].data, i, records[i]);
25220             if(n){
25221                 this.tpl.insertBefore(n, d);
25222             }else{
25223                 
25224                 this.tpl.append(this.el, d);
25225             }
25226         }
25227         this.updateIndexes(index);
25228     },
25229
25230     onRemove : function(ds, record, index){
25231        // Roo.log('onRemove');
25232         this.clearSelections();
25233         var el = this.dataName  ?
25234             this.el.child('.roo-tpl-' + this.dataName) :
25235             this.el; 
25236         
25237         el.dom.removeChild(this.nodes[index]);
25238         this.updateIndexes(index);
25239     },
25240
25241     /**
25242      * Refresh an individual node.
25243      * @param {Number} index
25244      */
25245     refreshNode : function(index){
25246         this.onUpdate(this.store, this.store.getAt(index));
25247     },
25248
25249     updateIndexes : function(startIndex, endIndex){
25250         var ns = this.nodes;
25251         startIndex = startIndex || 0;
25252         endIndex = endIndex || ns.length - 1;
25253         for(var i = startIndex; i <= endIndex; i++){
25254             ns[i].nodeIndex = i;
25255         }
25256     },
25257
25258     /**
25259      * Changes the data store this view uses and refresh the view.
25260      * @param {Store} store
25261      */
25262     setStore : function(store, initial){
25263         if(!initial && this.store){
25264             this.store.un("datachanged", this.refresh);
25265             this.store.un("add", this.onAdd);
25266             this.store.un("remove", this.onRemove);
25267             this.store.un("update", this.onUpdate);
25268             this.store.un("clear", this.refresh);
25269             this.store.un("beforeload", this.onBeforeLoad);
25270             this.store.un("load", this.onLoad);
25271             this.store.un("loadexception", this.onLoad);
25272         }
25273         if(store){
25274           
25275             store.on("datachanged", this.refresh, this);
25276             store.on("add", this.onAdd, this);
25277             store.on("remove", this.onRemove, this);
25278             store.on("update", this.onUpdate, this);
25279             store.on("clear", this.refresh, this);
25280             store.on("beforeload", this.onBeforeLoad, this);
25281             store.on("load", this.onLoad, this);
25282             store.on("loadexception", this.onLoad, this);
25283         }
25284         
25285         if(store){
25286             this.refresh();
25287         }
25288     },
25289     /**
25290      * onbeforeLoad - masks the loading area.
25291      *
25292      */
25293     onBeforeLoad : function(store,opts)
25294     {
25295          //Roo.log('onBeforeLoad');   
25296         if (!opts.add) {
25297             this.el.update("");
25298         }
25299         this.el.mask(this.mask ? this.mask : "Loading" ); 
25300     },
25301     onLoad : function ()
25302     {
25303         this.el.unmask();
25304     },
25305     
25306
25307     /**
25308      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25309      * @param {HTMLElement} node
25310      * @return {HTMLElement} The template node
25311      */
25312     findItemFromChild : function(node){
25313         var el = this.dataName  ?
25314             this.el.child('.roo-tpl-' + this.dataName,true) :
25315             this.el.dom; 
25316         
25317         if(!node || node.parentNode == el){
25318                     return node;
25319             }
25320             var p = node.parentNode;
25321             while(p && p != el){
25322             if(p.parentNode == el){
25323                 return p;
25324             }
25325             p = p.parentNode;
25326         }
25327             return null;
25328     },
25329
25330     /** @ignore */
25331     onClick : function(e){
25332         var item = this.findItemFromChild(e.getTarget());
25333         if(item){
25334             var index = this.indexOf(item);
25335             if(this.onItemClick(item, index, e) !== false){
25336                 this.fireEvent("click", this, index, item, e);
25337             }
25338         }else{
25339             this.clearSelections();
25340         }
25341     },
25342
25343     /** @ignore */
25344     onContextMenu : function(e){
25345         var item = this.findItemFromChild(e.getTarget());
25346         if(item){
25347             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25348         }
25349     },
25350
25351     /** @ignore */
25352     onDblClick : function(e){
25353         var item = this.findItemFromChild(e.getTarget());
25354         if(item){
25355             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25356         }
25357     },
25358
25359     onItemClick : function(item, index, e)
25360     {
25361         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25362             return false;
25363         }
25364         if (this.toggleSelect) {
25365             var m = this.isSelected(item) ? 'unselect' : 'select';
25366             //Roo.log(m);
25367             var _t = this;
25368             _t[m](item, true, false);
25369             return true;
25370         }
25371         if(this.multiSelect || this.singleSelect){
25372             if(this.multiSelect && e.shiftKey && this.lastSelection){
25373                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25374             }else{
25375                 this.select(item, this.multiSelect && e.ctrlKey);
25376                 this.lastSelection = item;
25377             }
25378             
25379             if(!this.tickable){
25380                 e.preventDefault();
25381             }
25382             
25383         }
25384         return true;
25385     },
25386
25387     /**
25388      * Get the number of selected nodes.
25389      * @return {Number}
25390      */
25391     getSelectionCount : function(){
25392         return this.selections.length;
25393     },
25394
25395     /**
25396      * Get the currently selected nodes.
25397      * @return {Array} An array of HTMLElements
25398      */
25399     getSelectedNodes : function(){
25400         return this.selections;
25401     },
25402
25403     /**
25404      * Get the indexes of the selected nodes.
25405      * @return {Array}
25406      */
25407     getSelectedIndexes : function(){
25408         var indexes = [], s = this.selections;
25409         for(var i = 0, len = s.length; i < len; i++){
25410             indexes.push(s[i].nodeIndex);
25411         }
25412         return indexes;
25413     },
25414
25415     /**
25416      * Clear all selections
25417      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25418      */
25419     clearSelections : function(suppressEvent){
25420         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25421             this.cmp.elements = this.selections;
25422             this.cmp.removeClass(this.selectedClass);
25423             this.selections = [];
25424             if(!suppressEvent){
25425                 this.fireEvent("selectionchange", this, this.selections);
25426             }
25427         }
25428     },
25429
25430     /**
25431      * Returns true if the passed node is selected
25432      * @param {HTMLElement/Number} node The node or node index
25433      * @return {Boolean}
25434      */
25435     isSelected : function(node){
25436         var s = this.selections;
25437         if(s.length < 1){
25438             return false;
25439         }
25440         node = this.getNode(node);
25441         return s.indexOf(node) !== -1;
25442     },
25443
25444     /**
25445      * Selects nodes.
25446      * @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
25447      * @param {Boolean} keepExisting (optional) true to keep existing selections
25448      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25449      */
25450     select : function(nodeInfo, keepExisting, suppressEvent){
25451         if(nodeInfo instanceof Array){
25452             if(!keepExisting){
25453                 this.clearSelections(true);
25454             }
25455             for(var i = 0, len = nodeInfo.length; i < len; i++){
25456                 this.select(nodeInfo[i], true, true);
25457             }
25458             return;
25459         } 
25460         var node = this.getNode(nodeInfo);
25461         if(!node || this.isSelected(node)){
25462             return; // already selected.
25463         }
25464         if(!keepExisting){
25465             this.clearSelections(true);
25466         }
25467         
25468         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25469             Roo.fly(node).addClass(this.selectedClass);
25470             this.selections.push(node);
25471             if(!suppressEvent){
25472                 this.fireEvent("selectionchange", this, this.selections);
25473             }
25474         }
25475         
25476         
25477     },
25478       /**
25479      * Unselects nodes.
25480      * @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
25481      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25483      */
25484     unselect : function(nodeInfo, keepExisting, suppressEvent)
25485     {
25486         if(nodeInfo instanceof Array){
25487             Roo.each(this.selections, function(s) {
25488                 this.unselect(s, nodeInfo);
25489             }, this);
25490             return;
25491         }
25492         var node = this.getNode(nodeInfo);
25493         if(!node || !this.isSelected(node)){
25494             //Roo.log("not selected");
25495             return; // not selected.
25496         }
25497         // fireevent???
25498         var ns = [];
25499         Roo.each(this.selections, function(s) {
25500             if (s == node ) {
25501                 Roo.fly(node).removeClass(this.selectedClass);
25502
25503                 return;
25504             }
25505             ns.push(s);
25506         },this);
25507         
25508         this.selections= ns;
25509         this.fireEvent("selectionchange", this, this.selections);
25510     },
25511
25512     /**
25513      * Gets a template node.
25514      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25515      * @return {HTMLElement} The node or null if it wasn't found
25516      */
25517     getNode : function(nodeInfo){
25518         if(typeof nodeInfo == "string"){
25519             return document.getElementById(nodeInfo);
25520         }else if(typeof nodeInfo == "number"){
25521             return this.nodes[nodeInfo];
25522         }
25523         return nodeInfo;
25524     },
25525
25526     /**
25527      * Gets a range template nodes.
25528      * @param {Number} startIndex
25529      * @param {Number} endIndex
25530      * @return {Array} An array of nodes
25531      */
25532     getNodes : function(start, end){
25533         var ns = this.nodes;
25534         start = start || 0;
25535         end = typeof end == "undefined" ? ns.length - 1 : end;
25536         var nodes = [];
25537         if(start <= end){
25538             for(var i = start; i <= end; i++){
25539                 nodes.push(ns[i]);
25540             }
25541         } else{
25542             for(var i = start; i >= end; i--){
25543                 nodes.push(ns[i]);
25544             }
25545         }
25546         return nodes;
25547     },
25548
25549     /**
25550      * Finds the index of the passed node
25551      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25552      * @return {Number} The index of the node or -1
25553      */
25554     indexOf : function(node){
25555         node = this.getNode(node);
25556         if(typeof node.nodeIndex == "number"){
25557             return node.nodeIndex;
25558         }
25559         var ns = this.nodes;
25560         for(var i = 0, len = ns.length; i < len; i++){
25561             if(ns[i] == node){
25562                 return i;
25563             }
25564         }
25565         return -1;
25566     }
25567 });
25568 /*
25569  * Based on:
25570  * Ext JS Library 1.1.1
25571  * Copyright(c) 2006-2007, Ext JS, LLC.
25572  *
25573  * Originally Released Under LGPL - original licence link has changed is not relivant.
25574  *
25575  * Fork - LGPL
25576  * <script type="text/javascript">
25577  */
25578
25579 /**
25580  * @class Roo.JsonView
25581  * @extends Roo.View
25582  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25583 <pre><code>
25584 var view = new Roo.JsonView({
25585     container: "my-element",
25586     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25587     multiSelect: true, 
25588     jsonRoot: "data" 
25589 });
25590
25591 // listen for node click?
25592 view.on("click", function(vw, index, node, e){
25593     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25594 });
25595
25596 // direct load of JSON data
25597 view.load("foobar.php");
25598
25599 // Example from my blog list
25600 var tpl = new Roo.Template(
25601     '&lt;div class="entry"&gt;' +
25602     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25603     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25604     "&lt;/div&gt;&lt;hr /&gt;"
25605 );
25606
25607 var moreView = new Roo.JsonView({
25608     container :  "entry-list", 
25609     template : tpl,
25610     jsonRoot: "posts"
25611 });
25612 moreView.on("beforerender", this.sortEntries, this);
25613 moreView.load({
25614     url: "/blog/get-posts.php",
25615     params: "allposts=true",
25616     text: "Loading Blog Entries..."
25617 });
25618 </code></pre>
25619
25620 * Note: old code is supported with arguments : (container, template, config)
25621
25622
25623  * @constructor
25624  * Create a new JsonView
25625  * 
25626  * @param {Object} config The config object
25627  * 
25628  */
25629 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25630     
25631     
25632     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25633
25634     var um = this.el.getUpdateManager();
25635     um.setRenderer(this);
25636     um.on("update", this.onLoad, this);
25637     um.on("failure", this.onLoadException, this);
25638
25639     /**
25640      * @event beforerender
25641      * Fires before rendering of the downloaded JSON data.
25642      * @param {Roo.JsonView} this
25643      * @param {Object} data The JSON data loaded
25644      */
25645     /**
25646      * @event load
25647      * Fires when data is loaded.
25648      * @param {Roo.JsonView} this
25649      * @param {Object} data The JSON data loaded
25650      * @param {Object} response The raw Connect response object
25651      */
25652     /**
25653      * @event loadexception
25654      * Fires when loading fails.
25655      * @param {Roo.JsonView} this
25656      * @param {Object} response The raw Connect response object
25657      */
25658     this.addEvents({
25659         'beforerender' : true,
25660         'load' : true,
25661         'loadexception' : true
25662     });
25663 };
25664 Roo.extend(Roo.JsonView, Roo.View, {
25665     /**
25666      * @type {String} The root property in the loaded JSON object that contains the data
25667      */
25668     jsonRoot : "",
25669
25670     /**
25671      * Refreshes the view.
25672      */
25673     refresh : function(){
25674         this.clearSelections();
25675         this.el.update("");
25676         var html = [];
25677         var o = this.jsonData;
25678         if(o && o.length > 0){
25679             for(var i = 0, len = o.length; i < len; i++){
25680                 var data = this.prepareData(o[i], i, o);
25681                 html[html.length] = this.tpl.apply(data);
25682             }
25683         }else{
25684             html.push(this.emptyText);
25685         }
25686         this.el.update(html.join(""));
25687         this.nodes = this.el.dom.childNodes;
25688         this.updateIndexes(0);
25689     },
25690
25691     /**
25692      * 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.
25693      * @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:
25694      <pre><code>
25695      view.load({
25696          url: "your-url.php",
25697          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25698          callback: yourFunction,
25699          scope: yourObject, //(optional scope)
25700          discardUrl: false,
25701          nocache: false,
25702          text: "Loading...",
25703          timeout: 30,
25704          scripts: false
25705      });
25706      </code></pre>
25707      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25708      * 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.
25709      * @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}
25710      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25711      * @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.
25712      */
25713     load : function(){
25714         var um = this.el.getUpdateManager();
25715         um.update.apply(um, arguments);
25716     },
25717
25718     render : function(el, response){
25719         this.clearSelections();
25720         this.el.update("");
25721         var o;
25722         try{
25723             o = Roo.util.JSON.decode(response.responseText);
25724             if(this.jsonRoot){
25725                 
25726                 o = o[this.jsonRoot];
25727             }
25728         } catch(e){
25729         }
25730         /**
25731          * The current JSON data or null
25732          */
25733         this.jsonData = o;
25734         this.beforeRender();
25735         this.refresh();
25736     },
25737
25738 /**
25739  * Get the number of records in the current JSON dataset
25740  * @return {Number}
25741  */
25742     getCount : function(){
25743         return this.jsonData ? this.jsonData.length : 0;
25744     },
25745
25746 /**
25747  * Returns the JSON object for the specified node(s)
25748  * @param {HTMLElement/Array} node The node or an array of nodes
25749  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25750  * you get the JSON object for the node
25751  */
25752     getNodeData : function(node){
25753         if(node instanceof Array){
25754             var data = [];
25755             for(var i = 0, len = node.length; i < len; i++){
25756                 data.push(this.getNodeData(node[i]));
25757             }
25758             return data;
25759         }
25760         return this.jsonData[this.indexOf(node)] || null;
25761     },
25762
25763     beforeRender : function(){
25764         this.snapshot = this.jsonData;
25765         if(this.sortInfo){
25766             this.sort.apply(this, this.sortInfo);
25767         }
25768         this.fireEvent("beforerender", this, this.jsonData);
25769     },
25770
25771     onLoad : function(el, o){
25772         this.fireEvent("load", this, this.jsonData, o);
25773     },
25774
25775     onLoadException : function(el, o){
25776         this.fireEvent("loadexception", this, o);
25777     },
25778
25779 /**
25780  * Filter the data by a specific property.
25781  * @param {String} property A property on your JSON objects
25782  * @param {String/RegExp} value Either string that the property values
25783  * should start with, or a RegExp to test against the property
25784  */
25785     filter : function(property, value){
25786         if(this.jsonData){
25787             var data = [];
25788             var ss = this.snapshot;
25789             if(typeof value == "string"){
25790                 var vlen = value.length;
25791                 if(vlen == 0){
25792                     this.clearFilter();
25793                     return;
25794                 }
25795                 value = value.toLowerCase();
25796                 for(var i = 0, len = ss.length; i < len; i++){
25797                     var o = ss[i];
25798                     if(o[property].substr(0, vlen).toLowerCase() == value){
25799                         data.push(o);
25800                     }
25801                 }
25802             } else if(value.exec){ // regex?
25803                 for(var i = 0, len = ss.length; i < len; i++){
25804                     var o = ss[i];
25805                     if(value.test(o[property])){
25806                         data.push(o);
25807                     }
25808                 }
25809             } else{
25810                 return;
25811             }
25812             this.jsonData = data;
25813             this.refresh();
25814         }
25815     },
25816
25817 /**
25818  * Filter by a function. The passed function will be called with each
25819  * object in the current dataset. If the function returns true the value is kept,
25820  * otherwise it is filtered.
25821  * @param {Function} fn
25822  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25823  */
25824     filterBy : function(fn, scope){
25825         if(this.jsonData){
25826             var data = [];
25827             var ss = this.snapshot;
25828             for(var i = 0, len = ss.length; i < len; i++){
25829                 var o = ss[i];
25830                 if(fn.call(scope || this, o)){
25831                     data.push(o);
25832                 }
25833             }
25834             this.jsonData = data;
25835             this.refresh();
25836         }
25837     },
25838
25839 /**
25840  * Clears the current filter.
25841  */
25842     clearFilter : function(){
25843         if(this.snapshot && this.jsonData != this.snapshot){
25844             this.jsonData = this.snapshot;
25845             this.refresh();
25846         }
25847     },
25848
25849
25850 /**
25851  * Sorts the data for this view and refreshes it.
25852  * @param {String} property A property on your JSON objects to sort on
25853  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25854  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25855  */
25856     sort : function(property, dir, sortType){
25857         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25858         if(this.jsonData){
25859             var p = property;
25860             var dsc = dir && dir.toLowerCase() == "desc";
25861             var f = function(o1, o2){
25862                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25863                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25864                 ;
25865                 if(v1 < v2){
25866                     return dsc ? +1 : -1;
25867                 } else if(v1 > v2){
25868                     return dsc ? -1 : +1;
25869                 } else{
25870                     return 0;
25871                 }
25872             };
25873             this.jsonData.sort(f);
25874             this.refresh();
25875             if(this.jsonData != this.snapshot){
25876                 this.snapshot.sort(f);
25877             }
25878         }
25879     }
25880 });/*
25881  * Based on:
25882  * Ext JS Library 1.1.1
25883  * Copyright(c) 2006-2007, Ext JS, LLC.
25884  *
25885  * Originally Released Under LGPL - original licence link has changed is not relivant.
25886  *
25887  * Fork - LGPL
25888  * <script type="text/javascript">
25889  */
25890  
25891
25892 /**
25893  * @class Roo.ColorPalette
25894  * @extends Roo.Component
25895  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25896  * Here's an example of typical usage:
25897  * <pre><code>
25898 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25899 cp.render('my-div');
25900
25901 cp.on('select', function(palette, selColor){
25902     // do something with selColor
25903 });
25904 </code></pre>
25905  * @constructor
25906  * Create a new ColorPalette
25907  * @param {Object} config The config object
25908  */
25909 Roo.ColorPalette = function(config){
25910     Roo.ColorPalette.superclass.constructor.call(this, config);
25911     this.addEvents({
25912         /**
25913              * @event select
25914              * Fires when a color is selected
25915              * @param {ColorPalette} this
25916              * @param {String} color The 6-digit color hex code (without the # symbol)
25917              */
25918         select: true
25919     });
25920
25921     if(this.handler){
25922         this.on("select", this.handler, this.scope, true);
25923     }
25924 };
25925 Roo.extend(Roo.ColorPalette, Roo.Component, {
25926     /**
25927      * @cfg {String} itemCls
25928      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25929      */
25930     itemCls : "x-color-palette",
25931     /**
25932      * @cfg {String} value
25933      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25934      * the hex codes are case-sensitive.
25935      */
25936     value : null,
25937     clickEvent:'click',
25938     // private
25939     ctype: "Roo.ColorPalette",
25940
25941     /**
25942      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25943      */
25944     allowReselect : false,
25945
25946     /**
25947      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25948      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25949      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25950      * of colors with the width setting until the box is symmetrical.</p>
25951      * <p>You can override individual colors if needed:</p>
25952      * <pre><code>
25953 var cp = new Roo.ColorPalette();
25954 cp.colors[0] = "FF0000";  // change the first box to red
25955 </code></pre>
25956
25957 Or you can provide a custom array of your own for complete control:
25958 <pre><code>
25959 var cp = new Roo.ColorPalette();
25960 cp.colors = ["000000", "993300", "333300"];
25961 </code></pre>
25962      * @type Array
25963      */
25964     colors : [
25965         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25966         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25967         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25968         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25969         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25970     ],
25971
25972     // private
25973     onRender : function(container, position){
25974         var t = new Roo.MasterTemplate(
25975             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25976         );
25977         var c = this.colors;
25978         for(var i = 0, len = c.length; i < len; i++){
25979             t.add([c[i]]);
25980         }
25981         var el = document.createElement("div");
25982         el.className = this.itemCls;
25983         t.overwrite(el);
25984         container.dom.insertBefore(el, position);
25985         this.el = Roo.get(el);
25986         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25987         if(this.clickEvent != 'click'){
25988             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25989         }
25990     },
25991
25992     // private
25993     afterRender : function(){
25994         Roo.ColorPalette.superclass.afterRender.call(this);
25995         if(this.value){
25996             var s = this.value;
25997             this.value = null;
25998             this.select(s);
25999         }
26000     },
26001
26002     // private
26003     handleClick : function(e, t){
26004         e.preventDefault();
26005         if(!this.disabled){
26006             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
26007             this.select(c.toUpperCase());
26008         }
26009     },
26010
26011     /**
26012      * Selects the specified color in the palette (fires the select event)
26013      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
26014      */
26015     select : function(color){
26016         color = color.replace("#", "");
26017         if(color != this.value || this.allowReselect){
26018             var el = this.el;
26019             if(this.value){
26020                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
26021             }
26022             el.child("a.color-"+color).addClass("x-color-palette-sel");
26023             this.value = color;
26024             this.fireEvent("select", this, color);
26025         }
26026     }
26027 });/*
26028  * Based on:
26029  * Ext JS Library 1.1.1
26030  * Copyright(c) 2006-2007, Ext JS, LLC.
26031  *
26032  * Originally Released Under LGPL - original licence link has changed is not relivant.
26033  *
26034  * Fork - LGPL
26035  * <script type="text/javascript">
26036  */
26037  
26038 /**
26039  * @class Roo.DatePicker
26040  * @extends Roo.Component
26041  * Simple date picker class.
26042  * @constructor
26043  * Create a new DatePicker
26044  * @param {Object} config The config object
26045  */
26046 Roo.DatePicker = function(config){
26047     Roo.DatePicker.superclass.constructor.call(this, config);
26048
26049     this.value = config && config.value ?
26050                  config.value.clearTime() : new Date().clearTime();
26051
26052     this.addEvents({
26053         /**
26054              * @event select
26055              * Fires when a date is selected
26056              * @param {DatePicker} this
26057              * @param {Date} date The selected date
26058              */
26059         'select': true,
26060         /**
26061              * @event monthchange
26062              * Fires when the displayed month changes 
26063              * @param {DatePicker} this
26064              * @param {Date} date The selected month
26065              */
26066         'monthchange': true
26067     });
26068
26069     if(this.handler){
26070         this.on("select", this.handler,  this.scope || this);
26071     }
26072     // build the disabledDatesRE
26073     if(!this.disabledDatesRE && this.disabledDates){
26074         var dd = this.disabledDates;
26075         var re = "(?:";
26076         for(var i = 0; i < dd.length; i++){
26077             re += dd[i];
26078             if(i != dd.length-1) re += "|";
26079         }
26080         this.disabledDatesRE = new RegExp(re + ")");
26081     }
26082 };
26083
26084 Roo.extend(Roo.DatePicker, Roo.Component, {
26085     /**
26086      * @cfg {String} todayText
26087      * The text to display on the button that selects the current date (defaults to "Today")
26088      */
26089     todayText : "Today",
26090     /**
26091      * @cfg {String} okText
26092      * The text to display on the ok button
26093      */
26094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26095     /**
26096      * @cfg {String} cancelText
26097      * The text to display on the cancel button
26098      */
26099     cancelText : "Cancel",
26100     /**
26101      * @cfg {String} todayTip
26102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26103      */
26104     todayTip : "{0} (Spacebar)",
26105     /**
26106      * @cfg {Date} minDate
26107      * Minimum allowable date (JavaScript date object, defaults to null)
26108      */
26109     minDate : null,
26110     /**
26111      * @cfg {Date} maxDate
26112      * Maximum allowable date (JavaScript date object, defaults to null)
26113      */
26114     maxDate : null,
26115     /**
26116      * @cfg {String} minText
26117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26118      */
26119     minText : "This date is before the minimum date",
26120     /**
26121      * @cfg {String} maxText
26122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26123      */
26124     maxText : "This date is after the maximum date",
26125     /**
26126      * @cfg {String} format
26127      * The default date format string which can be overriden for localization support.  The format must be
26128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26129      */
26130     format : "m/d/y",
26131     /**
26132      * @cfg {Array} disabledDays
26133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26134      */
26135     disabledDays : null,
26136     /**
26137      * @cfg {String} disabledDaysText
26138      * The tooltip to display when the date falls on a disabled day (defaults to "")
26139      */
26140     disabledDaysText : "",
26141     /**
26142      * @cfg {RegExp} disabledDatesRE
26143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26144      */
26145     disabledDatesRE : null,
26146     /**
26147      * @cfg {String} disabledDatesText
26148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26149      */
26150     disabledDatesText : "",
26151     /**
26152      * @cfg {Boolean} constrainToViewport
26153      * True to constrain the date picker to the viewport (defaults to true)
26154      */
26155     constrainToViewport : true,
26156     /**
26157      * @cfg {Array} monthNames
26158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26159      */
26160     monthNames : Date.monthNames,
26161     /**
26162      * @cfg {Array} dayNames
26163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26164      */
26165     dayNames : Date.dayNames,
26166     /**
26167      * @cfg {String} nextText
26168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26169      */
26170     nextText: 'Next Month (Control+Right)',
26171     /**
26172      * @cfg {String} prevText
26173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26174      */
26175     prevText: 'Previous Month (Control+Left)',
26176     /**
26177      * @cfg {String} monthYearText
26178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26179      */
26180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26181     /**
26182      * @cfg {Number} startDay
26183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26184      */
26185     startDay : 0,
26186     /**
26187      * @cfg {Bool} showClear
26188      * Show a clear button (usefull for date form elements that can be blank.)
26189      */
26190     
26191     showClear: false,
26192     
26193     /**
26194      * Sets the value of the date field
26195      * @param {Date} value The date to set
26196      */
26197     setValue : function(value){
26198         var old = this.value;
26199         
26200         if (typeof(value) == 'string') {
26201          
26202             value = Date.parseDate(value, this.format);
26203         }
26204         if (!value) {
26205             value = new Date();
26206         }
26207         
26208         this.value = value.clearTime(true);
26209         if(this.el){
26210             this.update(this.value);
26211         }
26212     },
26213
26214     /**
26215      * Gets the current selected value of the date field
26216      * @return {Date} The selected date
26217      */
26218     getValue : function(){
26219         return this.value;
26220     },
26221
26222     // private
26223     focus : function(){
26224         if(this.el){
26225             this.update(this.activeDate);
26226         }
26227     },
26228
26229     // privateval
26230     onRender : function(container, position){
26231         
26232         var m = [
26233              '<table cellspacing="0">',
26234                 '<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>',
26235                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26236         var dn = this.dayNames;
26237         for(var i = 0; i < 7; i++){
26238             var d = this.startDay+i;
26239             if(d > 6){
26240                 d = d-7;
26241             }
26242             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26243         }
26244         m[m.length] = "</tr></thead><tbody><tr>";
26245         for(var i = 0; i < 42; i++) {
26246             if(i % 7 == 0 && i != 0){
26247                 m[m.length] = "</tr><tr>";
26248             }
26249             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26250         }
26251         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26252             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26253
26254         var el = document.createElement("div");
26255         el.className = "x-date-picker";
26256         el.innerHTML = m.join("");
26257
26258         container.dom.insertBefore(el, position);
26259
26260         this.el = Roo.get(el);
26261         this.eventEl = Roo.get(el.firstChild);
26262
26263         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26264             handler: this.showPrevMonth,
26265             scope: this,
26266             preventDefault:true,
26267             stopDefault:true
26268         });
26269
26270         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26271             handler: this.showNextMonth,
26272             scope: this,
26273             preventDefault:true,
26274             stopDefault:true
26275         });
26276
26277         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26278
26279         this.monthPicker = this.el.down('div.x-date-mp');
26280         this.monthPicker.enableDisplayMode('block');
26281         
26282         var kn = new Roo.KeyNav(this.eventEl, {
26283             "left" : function(e){
26284                 e.ctrlKey ?
26285                     this.showPrevMonth() :
26286                     this.update(this.activeDate.add("d", -1));
26287             },
26288
26289             "right" : function(e){
26290                 e.ctrlKey ?
26291                     this.showNextMonth() :
26292                     this.update(this.activeDate.add("d", 1));
26293             },
26294
26295             "up" : function(e){
26296                 e.ctrlKey ?
26297                     this.showNextYear() :
26298                     this.update(this.activeDate.add("d", -7));
26299             },
26300
26301             "down" : function(e){
26302                 e.ctrlKey ?
26303                     this.showPrevYear() :
26304                     this.update(this.activeDate.add("d", 7));
26305             },
26306
26307             "pageUp" : function(e){
26308                 this.showNextMonth();
26309             },
26310
26311             "pageDown" : function(e){
26312                 this.showPrevMonth();
26313             },
26314
26315             "enter" : function(e){
26316                 e.stopPropagation();
26317                 return true;
26318             },
26319
26320             scope : this
26321         });
26322
26323         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26324
26325         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26326
26327         this.el.unselectable();
26328         
26329         this.cells = this.el.select("table.x-date-inner tbody td");
26330         this.textNodes = this.el.query("table.x-date-inner tbody span");
26331
26332         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26333             text: "&#160;",
26334             tooltip: this.monthYearText
26335         });
26336
26337         this.mbtn.on('click', this.showMonthPicker, this);
26338         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26339
26340
26341         var today = (new Date()).dateFormat(this.format);
26342         
26343         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26344         if (this.showClear) {
26345             baseTb.add( new Roo.Toolbar.Fill());
26346         }
26347         baseTb.add({
26348             text: String.format(this.todayText, today),
26349             tooltip: String.format(this.todayTip, today),
26350             handler: this.selectToday,
26351             scope: this
26352         });
26353         
26354         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26355             
26356         //});
26357         if (this.showClear) {
26358             
26359             baseTb.add( new Roo.Toolbar.Fill());
26360             baseTb.add({
26361                 text: '&#160;',
26362                 cls: 'x-btn-icon x-btn-clear',
26363                 handler: function() {
26364                     //this.value = '';
26365                     this.fireEvent("select", this, '');
26366                 },
26367                 scope: this
26368             });
26369         }
26370         
26371         
26372         if(Roo.isIE){
26373             this.el.repaint();
26374         }
26375         this.update(this.value);
26376     },
26377
26378     createMonthPicker : function(){
26379         if(!this.monthPicker.dom.firstChild){
26380             var buf = ['<table border="0" cellspacing="0">'];
26381             for(var i = 0; i < 6; i++){
26382                 buf.push(
26383                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26384                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26385                     i == 0 ?
26386                     '<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>' :
26387                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26388                 );
26389             }
26390             buf.push(
26391                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26392                     this.okText,
26393                     '</button><button type="button" class="x-date-mp-cancel">',
26394                     this.cancelText,
26395                     '</button></td></tr>',
26396                 '</table>'
26397             );
26398             this.monthPicker.update(buf.join(''));
26399             this.monthPicker.on('click', this.onMonthClick, this);
26400             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26401
26402             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26403             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26404
26405             this.mpMonths.each(function(m, a, i){
26406                 i += 1;
26407                 if((i%2) == 0){
26408                     m.dom.xmonth = 5 + Math.round(i * .5);
26409                 }else{
26410                     m.dom.xmonth = Math.round((i-1) * .5);
26411                 }
26412             });
26413         }
26414     },
26415
26416     showMonthPicker : function(){
26417         this.createMonthPicker();
26418         var size = this.el.getSize();
26419         this.monthPicker.setSize(size);
26420         this.monthPicker.child('table').setSize(size);
26421
26422         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26423         this.updateMPMonth(this.mpSelMonth);
26424         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26425         this.updateMPYear(this.mpSelYear);
26426
26427         this.monthPicker.slideIn('t', {duration:.2});
26428     },
26429
26430     updateMPYear : function(y){
26431         this.mpyear = y;
26432         var ys = this.mpYears.elements;
26433         for(var i = 1; i <= 10; i++){
26434             var td = ys[i-1], y2;
26435             if((i%2) == 0){
26436                 y2 = y + Math.round(i * .5);
26437                 td.firstChild.innerHTML = y2;
26438                 td.xyear = y2;
26439             }else{
26440                 y2 = y - (5-Math.round(i * .5));
26441                 td.firstChild.innerHTML = y2;
26442                 td.xyear = y2;
26443             }
26444             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26445         }
26446     },
26447
26448     updateMPMonth : function(sm){
26449         this.mpMonths.each(function(m, a, i){
26450             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26451         });
26452     },
26453
26454     selectMPMonth: function(m){
26455         
26456     },
26457
26458     onMonthClick : function(e, t){
26459         e.stopEvent();
26460         var el = new Roo.Element(t), pn;
26461         if(el.is('button.x-date-mp-cancel')){
26462             this.hideMonthPicker();
26463         }
26464         else if(el.is('button.x-date-mp-ok')){
26465             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26466             this.hideMonthPicker();
26467         }
26468         else if(pn = el.up('td.x-date-mp-month', 2)){
26469             this.mpMonths.removeClass('x-date-mp-sel');
26470             pn.addClass('x-date-mp-sel');
26471             this.mpSelMonth = pn.dom.xmonth;
26472         }
26473         else if(pn = el.up('td.x-date-mp-year', 2)){
26474             this.mpYears.removeClass('x-date-mp-sel');
26475             pn.addClass('x-date-mp-sel');
26476             this.mpSelYear = pn.dom.xyear;
26477         }
26478         else if(el.is('a.x-date-mp-prev')){
26479             this.updateMPYear(this.mpyear-10);
26480         }
26481         else if(el.is('a.x-date-mp-next')){
26482             this.updateMPYear(this.mpyear+10);
26483         }
26484     },
26485
26486     onMonthDblClick : function(e, t){
26487         e.stopEvent();
26488         var el = new Roo.Element(t), pn;
26489         if(pn = el.up('td.x-date-mp-month', 2)){
26490             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26491             this.hideMonthPicker();
26492         }
26493         else if(pn = el.up('td.x-date-mp-year', 2)){
26494             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26495             this.hideMonthPicker();
26496         }
26497     },
26498
26499     hideMonthPicker : function(disableAnim){
26500         if(this.monthPicker){
26501             if(disableAnim === true){
26502                 this.monthPicker.hide();
26503             }else{
26504                 this.monthPicker.slideOut('t', {duration:.2});
26505             }
26506         }
26507     },
26508
26509     // private
26510     showPrevMonth : function(e){
26511         this.update(this.activeDate.add("mo", -1));
26512     },
26513
26514     // private
26515     showNextMonth : function(e){
26516         this.update(this.activeDate.add("mo", 1));
26517     },
26518
26519     // private
26520     showPrevYear : function(){
26521         this.update(this.activeDate.add("y", -1));
26522     },
26523
26524     // private
26525     showNextYear : function(){
26526         this.update(this.activeDate.add("y", 1));
26527     },
26528
26529     // private
26530     handleMouseWheel : function(e){
26531         var delta = e.getWheelDelta();
26532         if(delta > 0){
26533             this.showPrevMonth();
26534             e.stopEvent();
26535         } else if(delta < 0){
26536             this.showNextMonth();
26537             e.stopEvent();
26538         }
26539     },
26540
26541     // private
26542     handleDateClick : function(e, t){
26543         e.stopEvent();
26544         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26545             this.setValue(new Date(t.dateValue));
26546             this.fireEvent("select", this, this.value);
26547         }
26548     },
26549
26550     // private
26551     selectToday : function(){
26552         this.setValue(new Date().clearTime());
26553         this.fireEvent("select", this, this.value);
26554     },
26555
26556     // private
26557     update : function(date)
26558     {
26559         var vd = this.activeDate;
26560         this.activeDate = date;
26561         if(vd && this.el){
26562             var t = date.getTime();
26563             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26564                 this.cells.removeClass("x-date-selected");
26565                 this.cells.each(function(c){
26566                    if(c.dom.firstChild.dateValue == t){
26567                        c.addClass("x-date-selected");
26568                        setTimeout(function(){
26569                             try{c.dom.firstChild.focus();}catch(e){}
26570                        }, 50);
26571                        return false;
26572                    }
26573                 });
26574                 return;
26575             }
26576         }
26577         
26578         var days = date.getDaysInMonth();
26579         var firstOfMonth = date.getFirstDateOfMonth();
26580         var startingPos = firstOfMonth.getDay()-this.startDay;
26581
26582         if(startingPos <= this.startDay){
26583             startingPos += 7;
26584         }
26585
26586         var pm = date.add("mo", -1);
26587         var prevStart = pm.getDaysInMonth()-startingPos;
26588
26589         var cells = this.cells.elements;
26590         var textEls = this.textNodes;
26591         days += startingPos;
26592
26593         // convert everything to numbers so it's fast
26594         var day = 86400000;
26595         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26596         var today = new Date().clearTime().getTime();
26597         var sel = date.clearTime().getTime();
26598         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26599         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26600         var ddMatch = this.disabledDatesRE;
26601         var ddText = this.disabledDatesText;
26602         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26603         var ddaysText = this.disabledDaysText;
26604         var format = this.format;
26605
26606         var setCellClass = function(cal, cell){
26607             cell.title = "";
26608             var t = d.getTime();
26609             cell.firstChild.dateValue = t;
26610             if(t == today){
26611                 cell.className += " x-date-today";
26612                 cell.title = cal.todayText;
26613             }
26614             if(t == sel){
26615                 cell.className += " x-date-selected";
26616                 setTimeout(function(){
26617                     try{cell.firstChild.focus();}catch(e){}
26618                 }, 50);
26619             }
26620             // disabling
26621             if(t < min) {
26622                 cell.className = " x-date-disabled";
26623                 cell.title = cal.minText;
26624                 return;
26625             }
26626             if(t > max) {
26627                 cell.className = " x-date-disabled";
26628                 cell.title = cal.maxText;
26629                 return;
26630             }
26631             if(ddays){
26632                 if(ddays.indexOf(d.getDay()) != -1){
26633                     cell.title = ddaysText;
26634                     cell.className = " x-date-disabled";
26635                 }
26636             }
26637             if(ddMatch && format){
26638                 var fvalue = d.dateFormat(format);
26639                 if(ddMatch.test(fvalue)){
26640                     cell.title = ddText.replace("%0", fvalue);
26641                     cell.className = " x-date-disabled";
26642                 }
26643             }
26644         };
26645
26646         var i = 0;
26647         for(; i < startingPos; i++) {
26648             textEls[i].innerHTML = (++prevStart);
26649             d.setDate(d.getDate()+1);
26650             cells[i].className = "x-date-prevday";
26651             setCellClass(this, cells[i]);
26652         }
26653         for(; i < days; i++){
26654             intDay = i - startingPos + 1;
26655             textEls[i].innerHTML = (intDay);
26656             d.setDate(d.getDate()+1);
26657             cells[i].className = "x-date-active";
26658             setCellClass(this, cells[i]);
26659         }
26660         var extraDays = 0;
26661         for(; i < 42; i++) {
26662              textEls[i].innerHTML = (++extraDays);
26663              d.setDate(d.getDate()+1);
26664              cells[i].className = "x-date-nextday";
26665              setCellClass(this, cells[i]);
26666         }
26667
26668         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26669         this.fireEvent('monthchange', this, date);
26670         
26671         if(!this.internalRender){
26672             var main = this.el.dom.firstChild;
26673             var w = main.offsetWidth;
26674             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26675             Roo.fly(main).setWidth(w);
26676             this.internalRender = true;
26677             // opera does not respect the auto grow header center column
26678             // then, after it gets a width opera refuses to recalculate
26679             // without a second pass
26680             if(Roo.isOpera && !this.secondPass){
26681                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26682                 this.secondPass = true;
26683                 this.update.defer(10, this, [date]);
26684             }
26685         }
26686         
26687         
26688     }
26689 });        /*
26690  * Based on:
26691  * Ext JS Library 1.1.1
26692  * Copyright(c) 2006-2007, Ext JS, LLC.
26693  *
26694  * Originally Released Under LGPL - original licence link has changed is not relivant.
26695  *
26696  * Fork - LGPL
26697  * <script type="text/javascript">
26698  */
26699 /**
26700  * @class Roo.TabPanel
26701  * @extends Roo.util.Observable
26702  * A lightweight tab container.
26703  * <br><br>
26704  * Usage:
26705  * <pre><code>
26706 // basic tabs 1, built from existing content
26707 var tabs = new Roo.TabPanel("tabs1");
26708 tabs.addTab("script", "View Script");
26709 tabs.addTab("markup", "View Markup");
26710 tabs.activate("script");
26711
26712 // more advanced tabs, built from javascript
26713 var jtabs = new Roo.TabPanel("jtabs");
26714 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26715
26716 // set up the UpdateManager
26717 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26718 var updater = tab2.getUpdateManager();
26719 updater.setDefaultUrl("ajax1.htm");
26720 tab2.on('activate', updater.refresh, updater, true);
26721
26722 // Use setUrl for Ajax loading
26723 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26724 tab3.setUrl("ajax2.htm", null, true);
26725
26726 // Disabled tab
26727 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26728 tab4.disable();
26729
26730 jtabs.activate("jtabs-1");
26731  * </code></pre>
26732  * @constructor
26733  * Create a new TabPanel.
26734  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26735  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26736  */
26737 Roo.TabPanel = function(container, config){
26738     /**
26739     * The container element for this TabPanel.
26740     * @type Roo.Element
26741     */
26742     this.el = Roo.get(container, true);
26743     if(config){
26744         if(typeof config == "boolean"){
26745             this.tabPosition = config ? "bottom" : "top";
26746         }else{
26747             Roo.apply(this, config);
26748         }
26749     }
26750     if(this.tabPosition == "bottom"){
26751         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26752         this.el.addClass("x-tabs-bottom");
26753     }
26754     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26755     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26756     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26757     if(Roo.isIE){
26758         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26759     }
26760     if(this.tabPosition != "bottom"){
26761         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26762          * @type Roo.Element
26763          */
26764         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26765         this.el.addClass("x-tabs-top");
26766     }
26767     this.items = [];
26768
26769     this.bodyEl.setStyle("position", "relative");
26770
26771     this.active = null;
26772     this.activateDelegate = this.activate.createDelegate(this);
26773
26774     this.addEvents({
26775         /**
26776          * @event tabchange
26777          * Fires when the active tab changes
26778          * @param {Roo.TabPanel} this
26779          * @param {Roo.TabPanelItem} activePanel The new active tab
26780          */
26781         "tabchange": true,
26782         /**
26783          * @event beforetabchange
26784          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26785          * @param {Roo.TabPanel} this
26786          * @param {Object} e Set cancel to true on this object to cancel the tab change
26787          * @param {Roo.TabPanelItem} tab The tab being changed to
26788          */
26789         "beforetabchange" : true
26790     });
26791
26792     Roo.EventManager.onWindowResize(this.onResize, this);
26793     this.cpad = this.el.getPadding("lr");
26794     this.hiddenCount = 0;
26795
26796
26797     // toolbar on the tabbar support...
26798     if (this.toolbar) {
26799         var tcfg = this.toolbar;
26800         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26801         this.toolbar = new Roo.Toolbar(tcfg);
26802         if (Roo.isSafari) {
26803             var tbl = tcfg.container.child('table', true);
26804             tbl.setAttribute('width', '100%');
26805         }
26806         
26807     }
26808    
26809
26810
26811     Roo.TabPanel.superclass.constructor.call(this);
26812 };
26813
26814 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26815     /*
26816      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26817      */
26818     tabPosition : "top",
26819     /*
26820      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26821      */
26822     currentTabWidth : 0,
26823     /*
26824      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26825      */
26826     minTabWidth : 40,
26827     /*
26828      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26829      */
26830     maxTabWidth : 250,
26831     /*
26832      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26833      */
26834     preferredTabWidth : 175,
26835     /*
26836      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26837      */
26838     resizeTabs : false,
26839     /*
26840      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26841      */
26842     monitorResize : true,
26843     /*
26844      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26845      */
26846     toolbar : false,
26847
26848     /**
26849      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26850      * @param {String} id The id of the div to use <b>or create</b>
26851      * @param {String} text The text for the tab
26852      * @param {String} content (optional) Content to put in the TabPanelItem body
26853      * @param {Boolean} closable (optional) True to create a close icon on the tab
26854      * @return {Roo.TabPanelItem} The created TabPanelItem
26855      */
26856     addTab : function(id, text, content, closable){
26857         var item = new Roo.TabPanelItem(this, id, text, closable);
26858         this.addTabItem(item);
26859         if(content){
26860             item.setContent(content);
26861         }
26862         return item;
26863     },
26864
26865     /**
26866      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26867      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26868      * @return {Roo.TabPanelItem}
26869      */
26870     getTab : function(id){
26871         return this.items[id];
26872     },
26873
26874     /**
26875      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26876      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26877      */
26878     hideTab : function(id){
26879         var t = this.items[id];
26880         if(!t.isHidden()){
26881            t.setHidden(true);
26882            this.hiddenCount++;
26883            this.autoSizeTabs();
26884         }
26885     },
26886
26887     /**
26888      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26889      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26890      */
26891     unhideTab : function(id){
26892         var t = this.items[id];
26893         if(t.isHidden()){
26894            t.setHidden(false);
26895            this.hiddenCount--;
26896            this.autoSizeTabs();
26897         }
26898     },
26899
26900     /**
26901      * Adds an existing {@link Roo.TabPanelItem}.
26902      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26903      */
26904     addTabItem : function(item){
26905         this.items[item.id] = item;
26906         this.items.push(item);
26907         if(this.resizeTabs){
26908            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26909            this.autoSizeTabs();
26910         }else{
26911             item.autoSize();
26912         }
26913     },
26914
26915     /**
26916      * Removes a {@link Roo.TabPanelItem}.
26917      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26918      */
26919     removeTab : function(id){
26920         var items = this.items;
26921         var tab = items[id];
26922         if(!tab) { return; }
26923         var index = items.indexOf(tab);
26924         if(this.active == tab && items.length > 1){
26925             var newTab = this.getNextAvailable(index);
26926             if(newTab) {
26927                 newTab.activate();
26928             }
26929         }
26930         this.stripEl.dom.removeChild(tab.pnode.dom);
26931         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26932             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26933         }
26934         items.splice(index, 1);
26935         delete this.items[tab.id];
26936         tab.fireEvent("close", tab);
26937         tab.purgeListeners();
26938         this.autoSizeTabs();
26939     },
26940
26941     getNextAvailable : function(start){
26942         var items = this.items;
26943         var index = start;
26944         // look for a next tab that will slide over to
26945         // replace the one being removed
26946         while(index < items.length){
26947             var item = items[++index];
26948             if(item && !item.isHidden()){
26949                 return item;
26950             }
26951         }
26952         // if one isn't found select the previous tab (on the left)
26953         index = start;
26954         while(index >= 0){
26955             var item = items[--index];
26956             if(item && !item.isHidden()){
26957                 return item;
26958             }
26959         }
26960         return null;
26961     },
26962
26963     /**
26964      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26965      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26966      */
26967     disableTab : function(id){
26968         var tab = this.items[id];
26969         if(tab && this.active != tab){
26970             tab.disable();
26971         }
26972     },
26973
26974     /**
26975      * Enables a {@link Roo.TabPanelItem} that is disabled.
26976      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26977      */
26978     enableTab : function(id){
26979         var tab = this.items[id];
26980         tab.enable();
26981     },
26982
26983     /**
26984      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26985      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26986      * @return {Roo.TabPanelItem} The TabPanelItem.
26987      */
26988     activate : function(id){
26989         var tab = this.items[id];
26990         if(!tab){
26991             return null;
26992         }
26993         if(tab == this.active || tab.disabled){
26994             return tab;
26995         }
26996         var e = {};
26997         this.fireEvent("beforetabchange", this, e, tab);
26998         if(e.cancel !== true && !tab.disabled){
26999             if(this.active){
27000                 this.active.hide();
27001             }
27002             this.active = this.items[id];
27003             this.active.show();
27004             this.fireEvent("tabchange", this, this.active);
27005         }
27006         return tab;
27007     },
27008
27009     /**
27010      * Gets the active {@link Roo.TabPanelItem}.
27011      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
27012      */
27013     getActiveTab : function(){
27014         return this.active;
27015     },
27016
27017     /**
27018      * Updates the tab body element to fit the height of the container element
27019      * for overflow scrolling
27020      * @param {Number} targetHeight (optional) Override the starting height from the elements height
27021      */
27022     syncHeight : function(targetHeight){
27023         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
27024         var bm = this.bodyEl.getMargins();
27025         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
27026         this.bodyEl.setHeight(newHeight);
27027         return newHeight;
27028     },
27029
27030     onResize : function(){
27031         if(this.monitorResize){
27032             this.autoSizeTabs();
27033         }
27034     },
27035
27036     /**
27037      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27038      */
27039     beginUpdate : function(){
27040         this.updating = true;
27041     },
27042
27043     /**
27044      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27045      */
27046     endUpdate : function(){
27047         this.updating = false;
27048         this.autoSizeTabs();
27049     },
27050
27051     /**
27052      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27053      */
27054     autoSizeTabs : function(){
27055         var count = this.items.length;
27056         var vcount = count - this.hiddenCount;
27057         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27058         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27059         var availWidth = Math.floor(w / vcount);
27060         var b = this.stripBody;
27061         if(b.getWidth() > w){
27062             var tabs = this.items;
27063             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27064             if(availWidth < this.minTabWidth){
27065                 /*if(!this.sleft){    // incomplete scrolling code
27066                     this.createScrollButtons();
27067                 }
27068                 this.showScroll();
27069                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27070             }
27071         }else{
27072             if(this.currentTabWidth < this.preferredTabWidth){
27073                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27074             }
27075         }
27076     },
27077
27078     /**
27079      * Returns the number of tabs in this TabPanel.
27080      * @return {Number}
27081      */
27082      getCount : function(){
27083          return this.items.length;
27084      },
27085
27086     /**
27087      * Resizes all the tabs to the passed width
27088      * @param {Number} The new width
27089      */
27090     setTabWidth : function(width){
27091         this.currentTabWidth = width;
27092         for(var i = 0, len = this.items.length; i < len; i++) {
27093                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27094         }
27095     },
27096
27097     /**
27098      * Destroys this TabPanel
27099      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27100      */
27101     destroy : function(removeEl){
27102         Roo.EventManager.removeResizeListener(this.onResize, this);
27103         for(var i = 0, len = this.items.length; i < len; i++){
27104             this.items[i].purgeListeners();
27105         }
27106         if(removeEl === true){
27107             this.el.update("");
27108             this.el.remove();
27109         }
27110     }
27111 });
27112
27113 /**
27114  * @class Roo.TabPanelItem
27115  * @extends Roo.util.Observable
27116  * Represents an individual item (tab plus body) in a TabPanel.
27117  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27118  * @param {String} id The id of this TabPanelItem
27119  * @param {String} text The text for the tab of this TabPanelItem
27120  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27121  */
27122 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27123     /**
27124      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27125      * @type Roo.TabPanel
27126      */
27127     this.tabPanel = tabPanel;
27128     /**
27129      * The id for this TabPanelItem
27130      * @type String
27131      */
27132     this.id = id;
27133     /** @private */
27134     this.disabled = false;
27135     /** @private */
27136     this.text = text;
27137     /** @private */
27138     this.loaded = false;
27139     this.closable = closable;
27140
27141     /**
27142      * The body element for this TabPanelItem.
27143      * @type Roo.Element
27144      */
27145     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27146     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27147     this.bodyEl.setStyle("display", "block");
27148     this.bodyEl.setStyle("zoom", "1");
27149     this.hideAction();
27150
27151     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27152     /** @private */
27153     this.el = Roo.get(els.el, true);
27154     this.inner = Roo.get(els.inner, true);
27155     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27156     this.pnode = Roo.get(els.el.parentNode, true);
27157     this.el.on("mousedown", this.onTabMouseDown, this);
27158     this.el.on("click", this.onTabClick, this);
27159     /** @private */
27160     if(closable){
27161         var c = Roo.get(els.close, true);
27162         c.dom.title = this.closeText;
27163         c.addClassOnOver("close-over");
27164         c.on("click", this.closeClick, this);
27165      }
27166
27167     this.addEvents({
27168          /**
27169          * @event activate
27170          * Fires when this tab becomes the active tab.
27171          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27172          * @param {Roo.TabPanelItem} this
27173          */
27174         "activate": true,
27175         /**
27176          * @event beforeclose
27177          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27178          * @param {Roo.TabPanelItem} this
27179          * @param {Object} e Set cancel to true on this object to cancel the close.
27180          */
27181         "beforeclose": true,
27182         /**
27183          * @event close
27184          * Fires when this tab is closed.
27185          * @param {Roo.TabPanelItem} this
27186          */
27187          "close": true,
27188         /**
27189          * @event deactivate
27190          * Fires when this tab is no longer the active tab.
27191          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27192          * @param {Roo.TabPanelItem} this
27193          */
27194          "deactivate" : true
27195     });
27196     this.hidden = false;
27197
27198     Roo.TabPanelItem.superclass.constructor.call(this);
27199 };
27200
27201 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27202     purgeListeners : function(){
27203        Roo.util.Observable.prototype.purgeListeners.call(this);
27204        this.el.removeAllListeners();
27205     },
27206     /**
27207      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27208      */
27209     show : function(){
27210         this.pnode.addClass("on");
27211         this.showAction();
27212         if(Roo.isOpera){
27213             this.tabPanel.stripWrap.repaint();
27214         }
27215         this.fireEvent("activate", this.tabPanel, this);
27216     },
27217
27218     /**
27219      * Returns true if this tab is the active tab.
27220      * @return {Boolean}
27221      */
27222     isActive : function(){
27223         return this.tabPanel.getActiveTab() == this;
27224     },
27225
27226     /**
27227      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27228      */
27229     hide : function(){
27230         this.pnode.removeClass("on");
27231         this.hideAction();
27232         this.fireEvent("deactivate", this.tabPanel, this);
27233     },
27234
27235     hideAction : function(){
27236         this.bodyEl.hide();
27237         this.bodyEl.setStyle("position", "absolute");
27238         this.bodyEl.setLeft("-20000px");
27239         this.bodyEl.setTop("-20000px");
27240     },
27241
27242     showAction : function(){
27243         this.bodyEl.setStyle("position", "relative");
27244         this.bodyEl.setTop("");
27245         this.bodyEl.setLeft("");
27246         this.bodyEl.show();
27247     },
27248
27249     /**
27250      * Set the tooltip for the tab.
27251      * @param {String} tooltip The tab's tooltip
27252      */
27253     setTooltip : function(text){
27254         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27255             this.textEl.dom.qtip = text;
27256             this.textEl.dom.removeAttribute('title');
27257         }else{
27258             this.textEl.dom.title = text;
27259         }
27260     },
27261
27262     onTabClick : function(e){
27263         e.preventDefault();
27264         this.tabPanel.activate(this.id);
27265     },
27266
27267     onTabMouseDown : function(e){
27268         e.preventDefault();
27269         this.tabPanel.activate(this.id);
27270     },
27271
27272     getWidth : function(){
27273         return this.inner.getWidth();
27274     },
27275
27276     setWidth : function(width){
27277         var iwidth = width - this.pnode.getPadding("lr");
27278         this.inner.setWidth(iwidth);
27279         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27280         this.pnode.setWidth(width);
27281     },
27282
27283     /**
27284      * Show or hide the tab
27285      * @param {Boolean} hidden True to hide or false to show.
27286      */
27287     setHidden : function(hidden){
27288         this.hidden = hidden;
27289         this.pnode.setStyle("display", hidden ? "none" : "");
27290     },
27291
27292     /**
27293      * Returns true if this tab is "hidden"
27294      * @return {Boolean}
27295      */
27296     isHidden : function(){
27297         return this.hidden;
27298     },
27299
27300     /**
27301      * Returns the text for this tab
27302      * @return {String}
27303      */
27304     getText : function(){
27305         return this.text;
27306     },
27307
27308     autoSize : function(){
27309         //this.el.beginMeasure();
27310         this.textEl.setWidth(1);
27311         /*
27312          *  #2804 [new] Tabs in Roojs
27313          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27314          */
27315         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27316         //this.el.endMeasure();
27317     },
27318
27319     /**
27320      * Sets the text for the tab (Note: this also sets the tooltip text)
27321      * @param {String} text The tab's text and tooltip
27322      */
27323     setText : function(text){
27324         this.text = text;
27325         this.textEl.update(text);
27326         this.setTooltip(text);
27327         if(!this.tabPanel.resizeTabs){
27328             this.autoSize();
27329         }
27330     },
27331     /**
27332      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27333      */
27334     activate : function(){
27335         this.tabPanel.activate(this.id);
27336     },
27337
27338     /**
27339      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27340      */
27341     disable : function(){
27342         if(this.tabPanel.active != this){
27343             this.disabled = true;
27344             this.pnode.addClass("disabled");
27345         }
27346     },
27347
27348     /**
27349      * Enables this TabPanelItem if it was previously disabled.
27350      */
27351     enable : function(){
27352         this.disabled = false;
27353         this.pnode.removeClass("disabled");
27354     },
27355
27356     /**
27357      * Sets the content for this TabPanelItem.
27358      * @param {String} content The content
27359      * @param {Boolean} loadScripts true to look for and load scripts
27360      */
27361     setContent : function(content, loadScripts){
27362         this.bodyEl.update(content, loadScripts);
27363     },
27364
27365     /**
27366      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27367      * @return {Roo.UpdateManager} The UpdateManager
27368      */
27369     getUpdateManager : function(){
27370         return this.bodyEl.getUpdateManager();
27371     },
27372
27373     /**
27374      * Set a URL to be used to load the content for this TabPanelItem.
27375      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27376      * @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)
27377      * @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)
27378      * @return {Roo.UpdateManager} The UpdateManager
27379      */
27380     setUrl : function(url, params, loadOnce){
27381         if(this.refreshDelegate){
27382             this.un('activate', this.refreshDelegate);
27383         }
27384         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27385         this.on("activate", this.refreshDelegate);
27386         return this.bodyEl.getUpdateManager();
27387     },
27388
27389     /** @private */
27390     _handleRefresh : function(url, params, loadOnce){
27391         if(!loadOnce || !this.loaded){
27392             var updater = this.bodyEl.getUpdateManager();
27393             updater.update(url, params, this._setLoaded.createDelegate(this));
27394         }
27395     },
27396
27397     /**
27398      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27399      *   Will fail silently if the setUrl method has not been called.
27400      *   This does not activate the panel, just updates its content.
27401      */
27402     refresh : function(){
27403         if(this.refreshDelegate){
27404            this.loaded = false;
27405            this.refreshDelegate();
27406         }
27407     },
27408
27409     /** @private */
27410     _setLoaded : function(){
27411         this.loaded = true;
27412     },
27413
27414     /** @private */
27415     closeClick : function(e){
27416         var o = {};
27417         e.stopEvent();
27418         this.fireEvent("beforeclose", this, o);
27419         if(o.cancel !== true){
27420             this.tabPanel.removeTab(this.id);
27421         }
27422     },
27423     /**
27424      * The text displayed in the tooltip for the close icon.
27425      * @type String
27426      */
27427     closeText : "Close this tab"
27428 });
27429
27430 /** @private */
27431 Roo.TabPanel.prototype.createStrip = function(container){
27432     var strip = document.createElement("div");
27433     strip.className = "x-tabs-wrap";
27434     container.appendChild(strip);
27435     return strip;
27436 };
27437 /** @private */
27438 Roo.TabPanel.prototype.createStripList = function(strip){
27439     // div wrapper for retard IE
27440     // returns the "tr" element.
27441     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27442         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27443         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27444     return strip.firstChild.firstChild.firstChild.firstChild;
27445 };
27446 /** @private */
27447 Roo.TabPanel.prototype.createBody = function(container){
27448     var body = document.createElement("div");
27449     Roo.id(body, "tab-body");
27450     Roo.fly(body).addClass("x-tabs-body");
27451     container.appendChild(body);
27452     return body;
27453 };
27454 /** @private */
27455 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27456     var body = Roo.getDom(id);
27457     if(!body){
27458         body = document.createElement("div");
27459         body.id = id;
27460     }
27461     Roo.fly(body).addClass("x-tabs-item-body");
27462     bodyEl.insertBefore(body, bodyEl.firstChild);
27463     return body;
27464 };
27465 /** @private */
27466 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27467     var td = document.createElement("td");
27468     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27469     //stripEl.appendChild(td);
27470     if(closable){
27471         td.className = "x-tabs-closable";
27472         if(!this.closeTpl){
27473             this.closeTpl = new Roo.Template(
27474                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27475                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27476                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27477             );
27478         }
27479         var el = this.closeTpl.overwrite(td, {"text": text});
27480         var close = el.getElementsByTagName("div")[0];
27481         var inner = el.getElementsByTagName("em")[0];
27482         return {"el": el, "close": close, "inner": inner};
27483     } else {
27484         if(!this.tabTpl){
27485             this.tabTpl = new Roo.Template(
27486                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27487                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27488             );
27489         }
27490         var el = this.tabTpl.overwrite(td, {"text": text});
27491         var inner = el.getElementsByTagName("em")[0];
27492         return {"el": el, "inner": inner};
27493     }
27494 };/*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504
27505 /**
27506  * @class Roo.Button
27507  * @extends Roo.util.Observable
27508  * Simple Button class
27509  * @cfg {String} text The button text
27510  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27511  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27512  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27513  * @cfg {Object} scope The scope of the handler
27514  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27515  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27516  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27517  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27518  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27519  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27520    applies if enableToggle = true)
27521  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27522  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27523   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27524  * @constructor
27525  * Create a new button
27526  * @param {Object} config The config object
27527  */
27528 Roo.Button = function(renderTo, config)
27529 {
27530     if (!config) {
27531         config = renderTo;
27532         renderTo = config.renderTo || false;
27533     }
27534     
27535     Roo.apply(this, config);
27536     this.addEvents({
27537         /**
27538              * @event click
27539              * Fires when this button is clicked
27540              * @param {Button} this
27541              * @param {EventObject} e The click event
27542              */
27543             "click" : true,
27544         /**
27545              * @event toggle
27546              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27547              * @param {Button} this
27548              * @param {Boolean} pressed
27549              */
27550             "toggle" : true,
27551         /**
27552              * @event mouseover
27553              * Fires when the mouse hovers over the button
27554              * @param {Button} this
27555              * @param {Event} e The event object
27556              */
27557         'mouseover' : true,
27558         /**
27559              * @event mouseout
27560              * Fires when the mouse exits the button
27561              * @param {Button} this
27562              * @param {Event} e The event object
27563              */
27564         'mouseout': true,
27565          /**
27566              * @event render
27567              * Fires when the button is rendered
27568              * @param {Button} this
27569              */
27570         'render': true
27571     });
27572     if(this.menu){
27573         this.menu = Roo.menu.MenuMgr.get(this.menu);
27574     }
27575     // register listeners first!!  - so render can be captured..
27576     Roo.util.Observable.call(this);
27577     if(renderTo){
27578         this.render(renderTo);
27579     }
27580     
27581   
27582 };
27583
27584 Roo.extend(Roo.Button, Roo.util.Observable, {
27585     /**
27586      * 
27587      */
27588     
27589     /**
27590      * Read-only. True if this button is hidden
27591      * @type Boolean
27592      */
27593     hidden : false,
27594     /**
27595      * Read-only. True if this button is disabled
27596      * @type Boolean
27597      */
27598     disabled : false,
27599     /**
27600      * Read-only. True if this button is pressed (only if enableToggle = true)
27601      * @type Boolean
27602      */
27603     pressed : false,
27604
27605     /**
27606      * @cfg {Number} tabIndex 
27607      * The DOM tabIndex for this button (defaults to undefined)
27608      */
27609     tabIndex : undefined,
27610
27611     /**
27612      * @cfg {Boolean} enableToggle
27613      * True to enable pressed/not pressed toggling (defaults to false)
27614      */
27615     enableToggle: false,
27616     /**
27617      * @cfg {Mixed} menu
27618      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27619      */
27620     menu : undefined,
27621     /**
27622      * @cfg {String} menuAlign
27623      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27624      */
27625     menuAlign : "tl-bl?",
27626
27627     /**
27628      * @cfg {String} iconCls
27629      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27630      */
27631     iconCls : undefined,
27632     /**
27633      * @cfg {String} type
27634      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27635      */
27636     type : 'button',
27637
27638     // private
27639     menuClassTarget: 'tr',
27640
27641     /**
27642      * @cfg {String} clickEvent
27643      * The type of event to map to the button's event handler (defaults to 'click')
27644      */
27645     clickEvent : 'click',
27646
27647     /**
27648      * @cfg {Boolean} handleMouseEvents
27649      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27650      */
27651     handleMouseEvents : true,
27652
27653     /**
27654      * @cfg {String} tooltipType
27655      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27656      */
27657     tooltipType : 'qtip',
27658
27659     /**
27660      * @cfg {String} cls
27661      * A CSS class to apply to the button's main element.
27662      */
27663     
27664     /**
27665      * @cfg {Roo.Template} template (Optional)
27666      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27667      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27668      * require code modifications if required elements (e.g. a button) aren't present.
27669      */
27670
27671     // private
27672     render : function(renderTo){
27673         var btn;
27674         if(this.hideParent){
27675             this.parentEl = Roo.get(renderTo);
27676         }
27677         if(!this.dhconfig){
27678             if(!this.template){
27679                 if(!Roo.Button.buttonTemplate){
27680                     // hideous table template
27681                     Roo.Button.buttonTemplate = new Roo.Template(
27682                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27683                         '<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>',
27684                         "</tr></tbody></table>");
27685                 }
27686                 this.template = Roo.Button.buttonTemplate;
27687             }
27688             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27689             var btnEl = btn.child("button:first");
27690             btnEl.on('focus', this.onFocus, this);
27691             btnEl.on('blur', this.onBlur, this);
27692             if(this.cls){
27693                 btn.addClass(this.cls);
27694             }
27695             if(this.icon){
27696                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27697             }
27698             if(this.iconCls){
27699                 btnEl.addClass(this.iconCls);
27700                 if(!this.cls){
27701                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27702                 }
27703             }
27704             if(this.tabIndex !== undefined){
27705                 btnEl.dom.tabIndex = this.tabIndex;
27706             }
27707             if(this.tooltip){
27708                 if(typeof this.tooltip == 'object'){
27709                     Roo.QuickTips.tips(Roo.apply({
27710                           target: btnEl.id
27711                     }, this.tooltip));
27712                 } else {
27713                     btnEl.dom[this.tooltipType] = this.tooltip;
27714                 }
27715             }
27716         }else{
27717             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27718         }
27719         this.el = btn;
27720         if(this.id){
27721             this.el.dom.id = this.el.id = this.id;
27722         }
27723         if(this.menu){
27724             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27725             this.menu.on("show", this.onMenuShow, this);
27726             this.menu.on("hide", this.onMenuHide, this);
27727         }
27728         btn.addClass("x-btn");
27729         if(Roo.isIE && !Roo.isIE7){
27730             this.autoWidth.defer(1, this);
27731         }else{
27732             this.autoWidth();
27733         }
27734         if(this.handleMouseEvents){
27735             btn.on("mouseover", this.onMouseOver, this);
27736             btn.on("mouseout", this.onMouseOut, this);
27737             btn.on("mousedown", this.onMouseDown, this);
27738         }
27739         btn.on(this.clickEvent, this.onClick, this);
27740         //btn.on("mouseup", this.onMouseUp, this);
27741         if(this.hidden){
27742             this.hide();
27743         }
27744         if(this.disabled){
27745             this.disable();
27746         }
27747         Roo.ButtonToggleMgr.register(this);
27748         if(this.pressed){
27749             this.el.addClass("x-btn-pressed");
27750         }
27751         if(this.repeat){
27752             var repeater = new Roo.util.ClickRepeater(btn,
27753                 typeof this.repeat == "object" ? this.repeat : {}
27754             );
27755             repeater.on("click", this.onClick,  this);
27756         }
27757         
27758         this.fireEvent('render', this);
27759         
27760     },
27761     /**
27762      * Returns the button's underlying element
27763      * @return {Roo.Element} The element
27764      */
27765     getEl : function(){
27766         return this.el;  
27767     },
27768     
27769     /**
27770      * Destroys this Button and removes any listeners.
27771      */
27772     destroy : function(){
27773         Roo.ButtonToggleMgr.unregister(this);
27774         this.el.removeAllListeners();
27775         this.purgeListeners();
27776         this.el.remove();
27777     },
27778
27779     // private
27780     autoWidth : function(){
27781         if(this.el){
27782             this.el.setWidth("auto");
27783             if(Roo.isIE7 && Roo.isStrict){
27784                 var ib = this.el.child('button');
27785                 if(ib && ib.getWidth() > 20){
27786                     ib.clip();
27787                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27788                 }
27789             }
27790             if(this.minWidth){
27791                 if(this.hidden){
27792                     this.el.beginMeasure();
27793                 }
27794                 if(this.el.getWidth() < this.minWidth){
27795                     this.el.setWidth(this.minWidth);
27796                 }
27797                 if(this.hidden){
27798                     this.el.endMeasure();
27799                 }
27800             }
27801         }
27802     },
27803
27804     /**
27805      * Assigns this button's click handler
27806      * @param {Function} handler The function to call when the button is clicked
27807      * @param {Object} scope (optional) Scope for the function passed in
27808      */
27809     setHandler : function(handler, scope){
27810         this.handler = handler;
27811         this.scope = scope;  
27812     },
27813     
27814     /**
27815      * Sets this button's text
27816      * @param {String} text The button text
27817      */
27818     setText : function(text){
27819         this.text = text;
27820         if(this.el){
27821             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27822         }
27823         this.autoWidth();
27824     },
27825     
27826     /**
27827      * Gets the text for this button
27828      * @return {String} The button text
27829      */
27830     getText : function(){
27831         return this.text;  
27832     },
27833     
27834     /**
27835      * Show this button
27836      */
27837     show: function(){
27838         this.hidden = false;
27839         if(this.el){
27840             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27841         }
27842     },
27843     
27844     /**
27845      * Hide this button
27846      */
27847     hide: function(){
27848         this.hidden = true;
27849         if(this.el){
27850             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27851         }
27852     },
27853     
27854     /**
27855      * Convenience function for boolean show/hide
27856      * @param {Boolean} visible True to show, false to hide
27857      */
27858     setVisible: function(visible){
27859         if(visible) {
27860             this.show();
27861         }else{
27862             this.hide();
27863         }
27864     },
27865     
27866     /**
27867      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27868      * @param {Boolean} state (optional) Force a particular state
27869      */
27870     toggle : function(state){
27871         state = state === undefined ? !this.pressed : state;
27872         if(state != this.pressed){
27873             if(state){
27874                 this.el.addClass("x-btn-pressed");
27875                 this.pressed = true;
27876                 this.fireEvent("toggle", this, true);
27877             }else{
27878                 this.el.removeClass("x-btn-pressed");
27879                 this.pressed = false;
27880                 this.fireEvent("toggle", this, false);
27881             }
27882             if(this.toggleHandler){
27883                 this.toggleHandler.call(this.scope || this, this, state);
27884             }
27885         }
27886     },
27887     
27888     /**
27889      * Focus the button
27890      */
27891     focus : function(){
27892         this.el.child('button:first').focus();
27893     },
27894     
27895     /**
27896      * Disable this button
27897      */
27898     disable : function(){
27899         if(this.el){
27900             this.el.addClass("x-btn-disabled");
27901         }
27902         this.disabled = true;
27903     },
27904     
27905     /**
27906      * Enable this button
27907      */
27908     enable : function(){
27909         if(this.el){
27910             this.el.removeClass("x-btn-disabled");
27911         }
27912         this.disabled = false;
27913     },
27914
27915     /**
27916      * Convenience function for boolean enable/disable
27917      * @param {Boolean} enabled True to enable, false to disable
27918      */
27919     setDisabled : function(v){
27920         this[v !== true ? "enable" : "disable"]();
27921     },
27922
27923     // private
27924     onClick : function(e)
27925     {
27926         if(e){
27927             e.preventDefault();
27928         }
27929         if(e.button != 0){
27930             return;
27931         }
27932         if(!this.disabled){
27933             if(this.enableToggle){
27934                 this.toggle();
27935             }
27936             if(this.menu && !this.menu.isVisible()){
27937                 this.menu.show(this.el, this.menuAlign);
27938             }
27939             this.fireEvent("click", this, e);
27940             if(this.handler){
27941                 this.el.removeClass("x-btn-over");
27942                 this.handler.call(this.scope || this, this, e);
27943             }
27944         }
27945     },
27946     // private
27947     onMouseOver : function(e){
27948         if(!this.disabled){
27949             this.el.addClass("x-btn-over");
27950             this.fireEvent('mouseover', this, e);
27951         }
27952     },
27953     // private
27954     onMouseOut : function(e){
27955         if(!e.within(this.el,  true)){
27956             this.el.removeClass("x-btn-over");
27957             this.fireEvent('mouseout', this, e);
27958         }
27959     },
27960     // private
27961     onFocus : function(e){
27962         if(!this.disabled){
27963             this.el.addClass("x-btn-focus");
27964         }
27965     },
27966     // private
27967     onBlur : function(e){
27968         this.el.removeClass("x-btn-focus");
27969     },
27970     // private
27971     onMouseDown : function(e){
27972         if(!this.disabled && e.button == 0){
27973             this.el.addClass("x-btn-click");
27974             Roo.get(document).on('mouseup', this.onMouseUp, this);
27975         }
27976     },
27977     // private
27978     onMouseUp : function(e){
27979         if(e.button == 0){
27980             this.el.removeClass("x-btn-click");
27981             Roo.get(document).un('mouseup', this.onMouseUp, this);
27982         }
27983     },
27984     // private
27985     onMenuShow : function(e){
27986         this.el.addClass("x-btn-menu-active");
27987     },
27988     // private
27989     onMenuHide : function(e){
27990         this.el.removeClass("x-btn-menu-active");
27991     }   
27992 });
27993
27994 // Private utility class used by Button
27995 Roo.ButtonToggleMgr = function(){
27996    var groups = {};
27997    
27998    function toggleGroup(btn, state){
27999        if(state){
28000            var g = groups[btn.toggleGroup];
28001            for(var i = 0, l = g.length; i < l; i++){
28002                if(g[i] != btn){
28003                    g[i].toggle(false);
28004                }
28005            }
28006        }
28007    }
28008    
28009    return {
28010        register : function(btn){
28011            if(!btn.toggleGroup){
28012                return;
28013            }
28014            var g = groups[btn.toggleGroup];
28015            if(!g){
28016                g = groups[btn.toggleGroup] = [];
28017            }
28018            g.push(btn);
28019            btn.on("toggle", toggleGroup);
28020        },
28021        
28022        unregister : function(btn){
28023            if(!btn.toggleGroup){
28024                return;
28025            }
28026            var g = groups[btn.toggleGroup];
28027            if(g){
28028                g.remove(btn);
28029                btn.un("toggle", toggleGroup);
28030            }
28031        }
28032    };
28033 }();/*
28034  * Based on:
28035  * Ext JS Library 1.1.1
28036  * Copyright(c) 2006-2007, Ext JS, LLC.
28037  *
28038  * Originally Released Under LGPL - original licence link has changed is not relivant.
28039  *
28040  * Fork - LGPL
28041  * <script type="text/javascript">
28042  */
28043  
28044 /**
28045  * @class Roo.SplitButton
28046  * @extends Roo.Button
28047  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28048  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28049  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28050  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28051  * @cfg {String} arrowTooltip The title attribute of the arrow
28052  * @constructor
28053  * Create a new menu button
28054  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28055  * @param {Object} config The config object
28056  */
28057 Roo.SplitButton = function(renderTo, config){
28058     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28059     /**
28060      * @event arrowclick
28061      * Fires when this button's arrow is clicked
28062      * @param {SplitButton} this
28063      * @param {EventObject} e The click event
28064      */
28065     this.addEvents({"arrowclick":true});
28066 };
28067
28068 Roo.extend(Roo.SplitButton, Roo.Button, {
28069     render : function(renderTo){
28070         // this is one sweet looking template!
28071         var tpl = new Roo.Template(
28072             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28073             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28074             '<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>',
28075             "</tbody></table></td><td>",
28076             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28077             '<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>',
28078             "</tbody></table></td></tr></table>"
28079         );
28080         var btn = tpl.append(renderTo, [this.text, this.type], true);
28081         var btnEl = btn.child("button");
28082         if(this.cls){
28083             btn.addClass(this.cls);
28084         }
28085         if(this.icon){
28086             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28087         }
28088         if(this.iconCls){
28089             btnEl.addClass(this.iconCls);
28090             if(!this.cls){
28091                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28092             }
28093         }
28094         this.el = btn;
28095         if(this.handleMouseEvents){
28096             btn.on("mouseover", this.onMouseOver, this);
28097             btn.on("mouseout", this.onMouseOut, this);
28098             btn.on("mousedown", this.onMouseDown, this);
28099             btn.on("mouseup", this.onMouseUp, this);
28100         }
28101         btn.on(this.clickEvent, this.onClick, this);
28102         if(this.tooltip){
28103             if(typeof this.tooltip == 'object'){
28104                 Roo.QuickTips.tips(Roo.apply({
28105                       target: btnEl.id
28106                 }, this.tooltip));
28107             } else {
28108                 btnEl.dom[this.tooltipType] = this.tooltip;
28109             }
28110         }
28111         if(this.arrowTooltip){
28112             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28113         }
28114         if(this.hidden){
28115             this.hide();
28116         }
28117         if(this.disabled){
28118             this.disable();
28119         }
28120         if(this.pressed){
28121             this.el.addClass("x-btn-pressed");
28122         }
28123         if(Roo.isIE && !Roo.isIE7){
28124             this.autoWidth.defer(1, this);
28125         }else{
28126             this.autoWidth();
28127         }
28128         if(this.menu){
28129             this.menu.on("show", this.onMenuShow, this);
28130             this.menu.on("hide", this.onMenuHide, this);
28131         }
28132         this.fireEvent('render', this);
28133     },
28134
28135     // private
28136     autoWidth : function(){
28137         if(this.el){
28138             var tbl = this.el.child("table:first");
28139             var tbl2 = this.el.child("table:last");
28140             this.el.setWidth("auto");
28141             tbl.setWidth("auto");
28142             if(Roo.isIE7 && Roo.isStrict){
28143                 var ib = this.el.child('button:first');
28144                 if(ib && ib.getWidth() > 20){
28145                     ib.clip();
28146                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28147                 }
28148             }
28149             if(this.minWidth){
28150                 if(this.hidden){
28151                     this.el.beginMeasure();
28152                 }
28153                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28154                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28155                 }
28156                 if(this.hidden){
28157                     this.el.endMeasure();
28158                 }
28159             }
28160             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28161         } 
28162     },
28163     /**
28164      * Sets this button's click handler
28165      * @param {Function} handler The function to call when the button is clicked
28166      * @param {Object} scope (optional) Scope for the function passed above
28167      */
28168     setHandler : function(handler, scope){
28169         this.handler = handler;
28170         this.scope = scope;  
28171     },
28172     
28173     /**
28174      * Sets this button's arrow click handler
28175      * @param {Function} handler The function to call when the arrow is clicked
28176      * @param {Object} scope (optional) Scope for the function passed above
28177      */
28178     setArrowHandler : function(handler, scope){
28179         this.arrowHandler = handler;
28180         this.scope = scope;  
28181     },
28182     
28183     /**
28184      * Focus the button
28185      */
28186     focus : function(){
28187         if(this.el){
28188             this.el.child("button:first").focus();
28189         }
28190     },
28191
28192     // private
28193     onClick : function(e){
28194         e.preventDefault();
28195         if(!this.disabled){
28196             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28197                 if(this.menu && !this.menu.isVisible()){
28198                     this.menu.show(this.el, this.menuAlign);
28199                 }
28200                 this.fireEvent("arrowclick", this, e);
28201                 if(this.arrowHandler){
28202                     this.arrowHandler.call(this.scope || this, this, e);
28203                 }
28204             }else{
28205                 this.fireEvent("click", this, e);
28206                 if(this.handler){
28207                     this.handler.call(this.scope || this, this, e);
28208                 }
28209             }
28210         }
28211     },
28212     // private
28213     onMouseDown : function(e){
28214         if(!this.disabled){
28215             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28216         }
28217     },
28218     // private
28219     onMouseUp : function(e){
28220         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28221     }   
28222 });
28223
28224
28225 // backwards compat
28226 Roo.MenuButton = Roo.SplitButton;/*
28227  * Based on:
28228  * Ext JS Library 1.1.1
28229  * Copyright(c) 2006-2007, Ext JS, LLC.
28230  *
28231  * Originally Released Under LGPL - original licence link has changed is not relivant.
28232  *
28233  * Fork - LGPL
28234  * <script type="text/javascript">
28235  */
28236
28237 /**
28238  * @class Roo.Toolbar
28239  * Basic Toolbar class.
28240  * @constructor
28241  * Creates a new Toolbar
28242  * @param {Object} container The config object
28243  */ 
28244 Roo.Toolbar = function(container, buttons, config)
28245 {
28246     /// old consturctor format still supported..
28247     if(container instanceof Array){ // omit the container for later rendering
28248         buttons = container;
28249         config = buttons;
28250         container = null;
28251     }
28252     if (typeof(container) == 'object' && container.xtype) {
28253         config = container;
28254         container = config.container;
28255         buttons = config.buttons || []; // not really - use items!!
28256     }
28257     var xitems = [];
28258     if (config && config.items) {
28259         xitems = config.items;
28260         delete config.items;
28261     }
28262     Roo.apply(this, config);
28263     this.buttons = buttons;
28264     
28265     if(container){
28266         this.render(container);
28267     }
28268     this.xitems = xitems;
28269     Roo.each(xitems, function(b) {
28270         this.add(b);
28271     }, this);
28272     
28273 };
28274
28275 Roo.Toolbar.prototype = {
28276     /**
28277      * @cfg {Array} items
28278      * array of button configs or elements to add (will be converted to a MixedCollection)
28279      */
28280     
28281     /**
28282      * @cfg {String/HTMLElement/Element} container
28283      * The id or element that will contain the toolbar
28284      */
28285     // private
28286     render : function(ct){
28287         this.el = Roo.get(ct);
28288         if(this.cls){
28289             this.el.addClass(this.cls);
28290         }
28291         // using a table allows for vertical alignment
28292         // 100% width is needed by Safari...
28293         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28294         this.tr = this.el.child("tr", true);
28295         var autoId = 0;
28296         this.items = new Roo.util.MixedCollection(false, function(o){
28297             return o.id || ("item" + (++autoId));
28298         });
28299         if(this.buttons){
28300             this.add.apply(this, this.buttons);
28301             delete this.buttons;
28302         }
28303     },
28304
28305     /**
28306      * Adds element(s) to the toolbar -- this function takes a variable number of 
28307      * arguments of mixed type and adds them to the toolbar.
28308      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28309      * <ul>
28310      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28311      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28312      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28313      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28314      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28315      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28316      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28317      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28318      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28319      * </ul>
28320      * @param {Mixed} arg2
28321      * @param {Mixed} etc.
28322      */
28323     add : function(){
28324         var a = arguments, l = a.length;
28325         for(var i = 0; i < l; i++){
28326             this._add(a[i]);
28327         }
28328     },
28329     // private..
28330     _add : function(el) {
28331         
28332         if (el.xtype) {
28333             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28334         }
28335         
28336         if (el.applyTo){ // some kind of form field
28337             return this.addField(el);
28338         } 
28339         if (el.render){ // some kind of Toolbar.Item
28340             return this.addItem(el);
28341         }
28342         if (typeof el == "string"){ // string
28343             if(el == "separator" || el == "-"){
28344                 return this.addSeparator();
28345             }
28346             if (el == " "){
28347                 return this.addSpacer();
28348             }
28349             if(el == "->"){
28350                 return this.addFill();
28351             }
28352             return this.addText(el);
28353             
28354         }
28355         if(el.tagName){ // element
28356             return this.addElement(el);
28357         }
28358         if(typeof el == "object"){ // must be button config?
28359             return this.addButton(el);
28360         }
28361         // and now what?!?!
28362         return false;
28363         
28364     },
28365     
28366     /**
28367      * Add an Xtype element
28368      * @param {Object} xtype Xtype Object
28369      * @return {Object} created Object
28370      */
28371     addxtype : function(e){
28372         return this.add(e);  
28373     },
28374     
28375     /**
28376      * Returns the Element for this toolbar.
28377      * @return {Roo.Element}
28378      */
28379     getEl : function(){
28380         return this.el;  
28381     },
28382     
28383     /**
28384      * Adds a separator
28385      * @return {Roo.Toolbar.Item} The separator item
28386      */
28387     addSeparator : function(){
28388         return this.addItem(new Roo.Toolbar.Separator());
28389     },
28390
28391     /**
28392      * Adds a spacer element
28393      * @return {Roo.Toolbar.Spacer} The spacer item
28394      */
28395     addSpacer : function(){
28396         return this.addItem(new Roo.Toolbar.Spacer());
28397     },
28398
28399     /**
28400      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28401      * @return {Roo.Toolbar.Fill} The fill item
28402      */
28403     addFill : function(){
28404         return this.addItem(new Roo.Toolbar.Fill());
28405     },
28406
28407     /**
28408      * Adds any standard HTML element to the toolbar
28409      * @param {String/HTMLElement/Element} el The element or id of the element to add
28410      * @return {Roo.Toolbar.Item} The element's item
28411      */
28412     addElement : function(el){
28413         return this.addItem(new Roo.Toolbar.Item(el));
28414     },
28415     /**
28416      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28417      * @type Roo.util.MixedCollection  
28418      */
28419     items : false,
28420      
28421     /**
28422      * Adds any Toolbar.Item or subclass
28423      * @param {Roo.Toolbar.Item} item
28424      * @return {Roo.Toolbar.Item} The item
28425      */
28426     addItem : function(item){
28427         var td = this.nextBlock();
28428         item.render(td);
28429         this.items.add(item);
28430         return item;
28431     },
28432     
28433     /**
28434      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28435      * @param {Object/Array} config A button config or array of configs
28436      * @return {Roo.Toolbar.Button/Array}
28437      */
28438     addButton : function(config){
28439         if(config instanceof Array){
28440             var buttons = [];
28441             for(var i = 0, len = config.length; i < len; i++) {
28442                 buttons.push(this.addButton(config[i]));
28443             }
28444             return buttons;
28445         }
28446         var b = config;
28447         if(!(config instanceof Roo.Toolbar.Button)){
28448             b = config.split ?
28449                 new Roo.Toolbar.SplitButton(config) :
28450                 new Roo.Toolbar.Button(config);
28451         }
28452         var td = this.nextBlock();
28453         b.render(td);
28454         this.items.add(b);
28455         return b;
28456     },
28457     
28458     /**
28459      * Adds text to the toolbar
28460      * @param {String} text The text to add
28461      * @return {Roo.Toolbar.Item} The element's item
28462      */
28463     addText : function(text){
28464         return this.addItem(new Roo.Toolbar.TextItem(text));
28465     },
28466     
28467     /**
28468      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28469      * @param {Number} index The index where the item is to be inserted
28470      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28471      * @return {Roo.Toolbar.Button/Item}
28472      */
28473     insertButton : function(index, item){
28474         if(item instanceof Array){
28475             var buttons = [];
28476             for(var i = 0, len = item.length; i < len; i++) {
28477                buttons.push(this.insertButton(index + i, item[i]));
28478             }
28479             return buttons;
28480         }
28481         if (!(item instanceof Roo.Toolbar.Button)){
28482            item = new Roo.Toolbar.Button(item);
28483         }
28484         var td = document.createElement("td");
28485         this.tr.insertBefore(td, this.tr.childNodes[index]);
28486         item.render(td);
28487         this.items.insert(index, item);
28488         return item;
28489     },
28490     
28491     /**
28492      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28493      * @param {Object} config
28494      * @return {Roo.Toolbar.Item} The element's item
28495      */
28496     addDom : function(config, returnEl){
28497         var td = this.nextBlock();
28498         Roo.DomHelper.overwrite(td, config);
28499         var ti = new Roo.Toolbar.Item(td.firstChild);
28500         ti.render(td);
28501         this.items.add(ti);
28502         return ti;
28503     },
28504
28505     /**
28506      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28507      * @type Roo.util.MixedCollection  
28508      */
28509     fields : false,
28510     
28511     /**
28512      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28513      * Note: the field should not have been rendered yet. For a field that has already been
28514      * rendered, use {@link #addElement}.
28515      * @param {Roo.form.Field} field
28516      * @return {Roo.ToolbarItem}
28517      */
28518      
28519       
28520     addField : function(field) {
28521         if (!this.fields) {
28522             var autoId = 0;
28523             this.fields = new Roo.util.MixedCollection(false, function(o){
28524                 return o.id || ("item" + (++autoId));
28525             });
28526
28527         }
28528         
28529         var td = this.nextBlock();
28530         field.render(td);
28531         var ti = new Roo.Toolbar.Item(td.firstChild);
28532         ti.render(td);
28533         this.items.add(ti);
28534         this.fields.add(field);
28535         return ti;
28536     },
28537     /**
28538      * Hide the toolbar
28539      * @method hide
28540      */
28541      
28542       
28543     hide : function()
28544     {
28545         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28546         this.el.child('div').hide();
28547     },
28548     /**
28549      * Show the toolbar
28550      * @method show
28551      */
28552     show : function()
28553     {
28554         this.el.child('div').show();
28555     },
28556       
28557     // private
28558     nextBlock : function(){
28559         var td = document.createElement("td");
28560         this.tr.appendChild(td);
28561         return td;
28562     },
28563
28564     // private
28565     destroy : function(){
28566         if(this.items){ // rendered?
28567             Roo.destroy.apply(Roo, this.items.items);
28568         }
28569         if(this.fields){ // rendered?
28570             Roo.destroy.apply(Roo, this.fields.items);
28571         }
28572         Roo.Element.uncache(this.el, this.tr);
28573     }
28574 };
28575
28576 /**
28577  * @class Roo.Toolbar.Item
28578  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28579  * @constructor
28580  * Creates a new Item
28581  * @param {HTMLElement} el 
28582  */
28583 Roo.Toolbar.Item = function(el){
28584     var cfg = {};
28585     if (typeof (el.xtype) != 'undefined') {
28586         cfg = el;
28587         el = cfg.el;
28588     }
28589     
28590     this.el = Roo.getDom(el);
28591     this.id = Roo.id(this.el);
28592     this.hidden = false;
28593     
28594     this.addEvents({
28595          /**
28596              * @event render
28597              * Fires when the button is rendered
28598              * @param {Button} this
28599              */
28600         'render': true
28601     });
28602     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28603 };
28604 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28605 //Roo.Toolbar.Item.prototype = {
28606     
28607     /**
28608      * Get this item's HTML Element
28609      * @return {HTMLElement}
28610      */
28611     getEl : function(){
28612        return this.el;  
28613     },
28614
28615     // private
28616     render : function(td){
28617         
28618          this.td = td;
28619         td.appendChild(this.el);
28620         
28621         this.fireEvent('render', this);
28622     },
28623     
28624     /**
28625      * Removes and destroys this item.
28626      */
28627     destroy : function(){
28628         this.td.parentNode.removeChild(this.td);
28629     },
28630     
28631     /**
28632      * Shows this item.
28633      */
28634     show: function(){
28635         this.hidden = false;
28636         this.td.style.display = "";
28637     },
28638     
28639     /**
28640      * Hides this item.
28641      */
28642     hide: function(){
28643         this.hidden = true;
28644         this.td.style.display = "none";
28645     },
28646     
28647     /**
28648      * Convenience function for boolean show/hide.
28649      * @param {Boolean} visible true to show/false to hide
28650      */
28651     setVisible: function(visible){
28652         if(visible) {
28653             this.show();
28654         }else{
28655             this.hide();
28656         }
28657     },
28658     
28659     /**
28660      * Try to focus this item.
28661      */
28662     focus : function(){
28663         Roo.fly(this.el).focus();
28664     },
28665     
28666     /**
28667      * Disables this item.
28668      */
28669     disable : function(){
28670         Roo.fly(this.td).addClass("x-item-disabled");
28671         this.disabled = true;
28672         this.el.disabled = true;
28673     },
28674     
28675     /**
28676      * Enables this item.
28677      */
28678     enable : function(){
28679         Roo.fly(this.td).removeClass("x-item-disabled");
28680         this.disabled = false;
28681         this.el.disabled = false;
28682     }
28683 });
28684
28685
28686 /**
28687  * @class Roo.Toolbar.Separator
28688  * @extends Roo.Toolbar.Item
28689  * A simple toolbar separator class
28690  * @constructor
28691  * Creates a new Separator
28692  */
28693 Roo.Toolbar.Separator = function(cfg){
28694     
28695     var s = document.createElement("span");
28696     s.className = "ytb-sep";
28697     if (cfg) {
28698         cfg.el = s;
28699     }
28700     
28701     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28702 };
28703 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28704     enable:Roo.emptyFn,
28705     disable:Roo.emptyFn,
28706     focus:Roo.emptyFn
28707 });
28708
28709 /**
28710  * @class Roo.Toolbar.Spacer
28711  * @extends Roo.Toolbar.Item
28712  * A simple element that adds extra horizontal space to a toolbar.
28713  * @constructor
28714  * Creates a new Spacer
28715  */
28716 Roo.Toolbar.Spacer = function(cfg){
28717     var s = document.createElement("div");
28718     s.className = "ytb-spacer";
28719     if (cfg) {
28720         cfg.el = s;
28721     }
28722     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28723 };
28724 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28725     enable:Roo.emptyFn,
28726     disable:Roo.emptyFn,
28727     focus:Roo.emptyFn
28728 });
28729
28730 /**
28731  * @class Roo.Toolbar.Fill
28732  * @extends Roo.Toolbar.Spacer
28733  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28734  * @constructor
28735  * Creates a new Spacer
28736  */
28737 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28738     // private
28739     render : function(td){
28740         td.style.width = '100%';
28741         Roo.Toolbar.Fill.superclass.render.call(this, td);
28742     }
28743 });
28744
28745 /**
28746  * @class Roo.Toolbar.TextItem
28747  * @extends Roo.Toolbar.Item
28748  * A simple class that renders text directly into a toolbar.
28749  * @constructor
28750  * Creates a new TextItem
28751  * @param {String} text
28752  */
28753 Roo.Toolbar.TextItem = function(cfg){
28754     var  text = cfg || "";
28755     if (typeof(cfg) == 'object') {
28756         text = cfg.text || "";
28757     }  else {
28758         cfg = null;
28759     }
28760     var s = document.createElement("span");
28761     s.className = "ytb-text";
28762     s.innerHTML = text;
28763     if (cfg) {
28764         cfg.el  = s;
28765     }
28766     
28767     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28768 };
28769 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28770     
28771      
28772     enable:Roo.emptyFn,
28773     disable:Roo.emptyFn,
28774     focus:Roo.emptyFn
28775 });
28776
28777 /**
28778  * @class Roo.Toolbar.Button
28779  * @extends Roo.Button
28780  * A button that renders into a toolbar.
28781  * @constructor
28782  * Creates a new Button
28783  * @param {Object} config A standard {@link Roo.Button} config object
28784  */
28785 Roo.Toolbar.Button = function(config){
28786     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28787 };
28788 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28789     render : function(td){
28790         this.td = td;
28791         Roo.Toolbar.Button.superclass.render.call(this, td);
28792     },
28793     
28794     /**
28795      * Removes and destroys this button
28796      */
28797     destroy : function(){
28798         Roo.Toolbar.Button.superclass.destroy.call(this);
28799         this.td.parentNode.removeChild(this.td);
28800     },
28801     
28802     /**
28803      * Shows this button
28804      */
28805     show: function(){
28806         this.hidden = false;
28807         this.td.style.display = "";
28808     },
28809     
28810     /**
28811      * Hides this button
28812      */
28813     hide: function(){
28814         this.hidden = true;
28815         this.td.style.display = "none";
28816     },
28817
28818     /**
28819      * Disables this item
28820      */
28821     disable : function(){
28822         Roo.fly(this.td).addClass("x-item-disabled");
28823         this.disabled = true;
28824     },
28825
28826     /**
28827      * Enables this item
28828      */
28829     enable : function(){
28830         Roo.fly(this.td).removeClass("x-item-disabled");
28831         this.disabled = false;
28832     }
28833 });
28834 // backwards compat
28835 Roo.ToolbarButton = Roo.Toolbar.Button;
28836
28837 /**
28838  * @class Roo.Toolbar.SplitButton
28839  * @extends Roo.SplitButton
28840  * A menu button that renders into a toolbar.
28841  * @constructor
28842  * Creates a new SplitButton
28843  * @param {Object} config A standard {@link Roo.SplitButton} config object
28844  */
28845 Roo.Toolbar.SplitButton = function(config){
28846     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28847 };
28848 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28849     render : function(td){
28850         this.td = td;
28851         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28852     },
28853     
28854     /**
28855      * Removes and destroys this button
28856      */
28857     destroy : function(){
28858         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28859         this.td.parentNode.removeChild(this.td);
28860     },
28861     
28862     /**
28863      * Shows this button
28864      */
28865     show: function(){
28866         this.hidden = false;
28867         this.td.style.display = "";
28868     },
28869     
28870     /**
28871      * Hides this button
28872      */
28873     hide: function(){
28874         this.hidden = true;
28875         this.td.style.display = "none";
28876     }
28877 });
28878
28879 // backwards compat
28880 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28881  * Based on:
28882  * Ext JS Library 1.1.1
28883  * Copyright(c) 2006-2007, Ext JS, LLC.
28884  *
28885  * Originally Released Under LGPL - original licence link has changed is not relivant.
28886  *
28887  * Fork - LGPL
28888  * <script type="text/javascript">
28889  */
28890  
28891 /**
28892  * @class Roo.PagingToolbar
28893  * @extends Roo.Toolbar
28894  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28895  * @constructor
28896  * Create a new PagingToolbar
28897  * @param {Object} config The config object
28898  */
28899 Roo.PagingToolbar = function(el, ds, config)
28900 {
28901     // old args format still supported... - xtype is prefered..
28902     if (typeof(el) == 'object' && el.xtype) {
28903         // created from xtype...
28904         config = el;
28905         ds = el.dataSource;
28906         el = config.container;
28907     }
28908     var items = [];
28909     if (config.items) {
28910         items = config.items;
28911         config.items = [];
28912     }
28913     
28914     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28915     this.ds = ds;
28916     this.cursor = 0;
28917     this.renderButtons(this.el);
28918     this.bind(ds);
28919     
28920     // supprot items array.
28921    
28922     Roo.each(items, function(e) {
28923         this.add(Roo.factory(e));
28924     },this);
28925     
28926 };
28927
28928 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28929     /**
28930      * @cfg {Roo.data.Store} dataSource
28931      * The underlying data store providing the paged data
28932      */
28933     /**
28934      * @cfg {String/HTMLElement/Element} container
28935      * container The id or element that will contain the toolbar
28936      */
28937     /**
28938      * @cfg {Boolean} displayInfo
28939      * True to display the displayMsg (defaults to false)
28940      */
28941     /**
28942      * @cfg {Number} pageSize
28943      * The number of records to display per page (defaults to 20)
28944      */
28945     pageSize: 20,
28946     /**
28947      * @cfg {String} displayMsg
28948      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28949      */
28950     displayMsg : 'Displaying {0} - {1} of {2}',
28951     /**
28952      * @cfg {String} emptyMsg
28953      * The message to display when no records are found (defaults to "No data to display")
28954      */
28955     emptyMsg : 'No data to display',
28956     /**
28957      * Customizable piece of the default paging text (defaults to "Page")
28958      * @type String
28959      */
28960     beforePageText : "Page",
28961     /**
28962      * Customizable piece of the default paging text (defaults to "of %0")
28963      * @type String
28964      */
28965     afterPageText : "of {0}",
28966     /**
28967      * Customizable piece of the default paging text (defaults to "First Page")
28968      * @type String
28969      */
28970     firstText : "First Page",
28971     /**
28972      * Customizable piece of the default paging text (defaults to "Previous Page")
28973      * @type String
28974      */
28975     prevText : "Previous Page",
28976     /**
28977      * Customizable piece of the default paging text (defaults to "Next Page")
28978      * @type String
28979      */
28980     nextText : "Next Page",
28981     /**
28982      * Customizable piece of the default paging text (defaults to "Last Page")
28983      * @type String
28984      */
28985     lastText : "Last Page",
28986     /**
28987      * Customizable piece of the default paging text (defaults to "Refresh")
28988      * @type String
28989      */
28990     refreshText : "Refresh",
28991
28992     // private
28993     renderButtons : function(el){
28994         Roo.PagingToolbar.superclass.render.call(this, el);
28995         this.first = this.addButton({
28996             tooltip: this.firstText,
28997             cls: "x-btn-icon x-grid-page-first",
28998             disabled: true,
28999             handler: this.onClick.createDelegate(this, ["first"])
29000         });
29001         this.prev = this.addButton({
29002             tooltip: this.prevText,
29003             cls: "x-btn-icon x-grid-page-prev",
29004             disabled: true,
29005             handler: this.onClick.createDelegate(this, ["prev"])
29006         });
29007         //this.addSeparator();
29008         this.add(this.beforePageText);
29009         this.field = Roo.get(this.addDom({
29010            tag: "input",
29011            type: "text",
29012            size: "3",
29013            value: "1",
29014            cls: "x-grid-page-number"
29015         }).el);
29016         this.field.on("keydown", this.onPagingKeydown, this);
29017         this.field.on("focus", function(){this.dom.select();});
29018         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
29019         this.field.setHeight(18);
29020         //this.addSeparator();
29021         this.next = this.addButton({
29022             tooltip: this.nextText,
29023             cls: "x-btn-icon x-grid-page-next",
29024             disabled: true,
29025             handler: this.onClick.createDelegate(this, ["next"])
29026         });
29027         this.last = this.addButton({
29028             tooltip: this.lastText,
29029             cls: "x-btn-icon x-grid-page-last",
29030             disabled: true,
29031             handler: this.onClick.createDelegate(this, ["last"])
29032         });
29033         //this.addSeparator();
29034         this.loading = this.addButton({
29035             tooltip: this.refreshText,
29036             cls: "x-btn-icon x-grid-loading",
29037             handler: this.onClick.createDelegate(this, ["refresh"])
29038         });
29039
29040         if(this.displayInfo){
29041             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29042         }
29043     },
29044
29045     // private
29046     updateInfo : function(){
29047         if(this.displayEl){
29048             var count = this.ds.getCount();
29049             var msg = count == 0 ?
29050                 this.emptyMsg :
29051                 String.format(
29052                     this.displayMsg,
29053                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29054                 );
29055             this.displayEl.update(msg);
29056         }
29057     },
29058
29059     // private
29060     onLoad : function(ds, r, o){
29061        this.cursor = o.params ? o.params.start : 0;
29062        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29063
29064        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29065        this.field.dom.value = ap;
29066        this.first.setDisabled(ap == 1);
29067        this.prev.setDisabled(ap == 1);
29068        this.next.setDisabled(ap == ps);
29069        this.last.setDisabled(ap == ps);
29070        this.loading.enable();
29071        this.updateInfo();
29072     },
29073
29074     // private
29075     getPageData : function(){
29076         var total = this.ds.getTotalCount();
29077         return {
29078             total : total,
29079             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29080             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29081         };
29082     },
29083
29084     // private
29085     onLoadError : function(){
29086         this.loading.enable();
29087     },
29088
29089     // private
29090     onPagingKeydown : function(e){
29091         var k = e.getKey();
29092         var d = this.getPageData();
29093         if(k == e.RETURN){
29094             var v = this.field.dom.value, pageNum;
29095             if(!v || isNaN(pageNum = parseInt(v, 10))){
29096                 this.field.dom.value = d.activePage;
29097                 return;
29098             }
29099             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29100             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29101             e.stopEvent();
29102         }
29103         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))
29104         {
29105           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29106           this.field.dom.value = pageNum;
29107           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29108           e.stopEvent();
29109         }
29110         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29111         {
29112           var v = this.field.dom.value, pageNum; 
29113           var increment = (e.shiftKey) ? 10 : 1;
29114           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29115             increment *= -1;
29116           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29117             this.field.dom.value = d.activePage;
29118             return;
29119           }
29120           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29121           {
29122             this.field.dom.value = parseInt(v, 10) + increment;
29123             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29124             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29125           }
29126           e.stopEvent();
29127         }
29128     },
29129
29130     // private
29131     beforeLoad : function(){
29132         if(this.loading){
29133             this.loading.disable();
29134         }
29135     },
29136
29137     // private
29138     onClick : function(which){
29139         var ds = this.ds;
29140         switch(which){
29141             case "first":
29142                 ds.load({params:{start: 0, limit: this.pageSize}});
29143             break;
29144             case "prev":
29145                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29146             break;
29147             case "next":
29148                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29149             break;
29150             case "last":
29151                 var total = ds.getTotalCount();
29152                 var extra = total % this.pageSize;
29153                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29154                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29155             break;
29156             case "refresh":
29157                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29158             break;
29159         }
29160     },
29161
29162     /**
29163      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29164      * @param {Roo.data.Store} store The data store to unbind
29165      */
29166     unbind : function(ds){
29167         ds.un("beforeload", this.beforeLoad, this);
29168         ds.un("load", this.onLoad, this);
29169         ds.un("loadexception", this.onLoadError, this);
29170         ds.un("remove", this.updateInfo, this);
29171         ds.un("add", this.updateInfo, this);
29172         this.ds = undefined;
29173     },
29174
29175     /**
29176      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29177      * @param {Roo.data.Store} store The data store to bind
29178      */
29179     bind : function(ds){
29180         ds.on("beforeload", this.beforeLoad, this);
29181         ds.on("load", this.onLoad, this);
29182         ds.on("loadexception", this.onLoadError, this);
29183         ds.on("remove", this.updateInfo, this);
29184         ds.on("add", this.updateInfo, this);
29185         this.ds = ds;
29186     }
29187 });/*
29188  * Based on:
29189  * Ext JS Library 1.1.1
29190  * Copyright(c) 2006-2007, Ext JS, LLC.
29191  *
29192  * Originally Released Under LGPL - original licence link has changed is not relivant.
29193  *
29194  * Fork - LGPL
29195  * <script type="text/javascript">
29196  */
29197
29198 /**
29199  * @class Roo.Resizable
29200  * @extends Roo.util.Observable
29201  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29202  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29203  * 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
29204  * the element will be wrapped for you automatically.</p>
29205  * <p>Here is the list of valid resize handles:</p>
29206  * <pre>
29207 Value   Description
29208 ------  -------------------
29209  'n'     north
29210  's'     south
29211  'e'     east
29212  'w'     west
29213  'nw'    northwest
29214  'sw'    southwest
29215  'se'    southeast
29216  'ne'    northeast
29217  'hd'    horizontal drag
29218  'all'   all
29219 </pre>
29220  * <p>Here's an example showing the creation of a typical Resizable:</p>
29221  * <pre><code>
29222 var resizer = new Roo.Resizable("element-id", {
29223     handles: 'all',
29224     minWidth: 200,
29225     minHeight: 100,
29226     maxWidth: 500,
29227     maxHeight: 400,
29228     pinned: true
29229 });
29230 resizer.on("resize", myHandler);
29231 </code></pre>
29232  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29233  * resizer.east.setDisplayed(false);</p>
29234  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29235  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29236  * resize operation's new size (defaults to [0, 0])
29237  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29238  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29239  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29240  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29241  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29242  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29243  * @cfg {Number} width The width of the element in pixels (defaults to null)
29244  * @cfg {Number} height The height of the element in pixels (defaults to null)
29245  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29246  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29247  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29248  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29249  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29250  * in favor of the handles config option (defaults to false)
29251  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29252  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29253  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29254  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29255  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29256  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29257  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29258  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29259  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29260  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29261  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29262  * @constructor
29263  * Create a new resizable component
29264  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29265  * @param {Object} config configuration options
29266   */
29267 Roo.Resizable = function(el, config)
29268 {
29269     this.el = Roo.get(el);
29270
29271     if(config && config.wrap){
29272         config.resizeChild = this.el;
29273         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29274         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29275         this.el.setStyle("overflow", "hidden");
29276         this.el.setPositioning(config.resizeChild.getPositioning());
29277         config.resizeChild.clearPositioning();
29278         if(!config.width || !config.height){
29279             var csize = config.resizeChild.getSize();
29280             this.el.setSize(csize.width, csize.height);
29281         }
29282         if(config.pinned && !config.adjustments){
29283             config.adjustments = "auto";
29284         }
29285     }
29286
29287     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29288     this.proxy.unselectable();
29289     this.proxy.enableDisplayMode('block');
29290
29291     Roo.apply(this, config);
29292
29293     if(this.pinned){
29294         this.disableTrackOver = true;
29295         this.el.addClass("x-resizable-pinned");
29296     }
29297     // if the element isn't positioned, make it relative
29298     var position = this.el.getStyle("position");
29299     if(position != "absolute" && position != "fixed"){
29300         this.el.setStyle("position", "relative");
29301     }
29302     if(!this.handles){ // no handles passed, must be legacy style
29303         this.handles = 's,e,se';
29304         if(this.multiDirectional){
29305             this.handles += ',n,w';
29306         }
29307     }
29308     if(this.handles == "all"){
29309         this.handles = "n s e w ne nw se sw";
29310     }
29311     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29312     var ps = Roo.Resizable.positions;
29313     for(var i = 0, len = hs.length; i < len; i++){
29314         if(hs[i] && ps[hs[i]]){
29315             var pos = ps[hs[i]];
29316             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29317         }
29318     }
29319     // legacy
29320     this.corner = this.southeast;
29321     
29322     // updateBox = the box can move..
29323     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29324         this.updateBox = true;
29325     }
29326
29327     this.activeHandle = null;
29328
29329     if(this.resizeChild){
29330         if(typeof this.resizeChild == "boolean"){
29331             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29332         }else{
29333             this.resizeChild = Roo.get(this.resizeChild, true);
29334         }
29335     }
29336     
29337     if(this.adjustments == "auto"){
29338         var rc = this.resizeChild;
29339         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29340         if(rc && (hw || hn)){
29341             rc.position("relative");
29342             rc.setLeft(hw ? hw.el.getWidth() : 0);
29343             rc.setTop(hn ? hn.el.getHeight() : 0);
29344         }
29345         this.adjustments = [
29346             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29347             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29348         ];
29349     }
29350
29351     if(this.draggable){
29352         this.dd = this.dynamic ?
29353             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29354         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29355     }
29356
29357     // public events
29358     this.addEvents({
29359         /**
29360          * @event beforeresize
29361          * Fired before resize is allowed. Set enabled to false to cancel resize.
29362          * @param {Roo.Resizable} this
29363          * @param {Roo.EventObject} e The mousedown event
29364          */
29365         "beforeresize" : true,
29366         /**
29367          * @event resizing
29368          * Fired a resizing.
29369          * @param {Roo.Resizable} this
29370          * @param {Number} x The new x position
29371          * @param {Number} y The new y position
29372          * @param {Number} w The new w width
29373          * @param {Number} h The new h hight
29374          * @param {Roo.EventObject} e The mouseup event
29375          */
29376         "resizing" : true,
29377         /**
29378          * @event resize
29379          * Fired after a resize.
29380          * @param {Roo.Resizable} this
29381          * @param {Number} width The new width
29382          * @param {Number} height The new height
29383          * @param {Roo.EventObject} e The mouseup event
29384          */
29385         "resize" : true
29386     });
29387
29388     if(this.width !== null && this.height !== null){
29389         this.resizeTo(this.width, this.height);
29390     }else{
29391         this.updateChildSize();
29392     }
29393     if(Roo.isIE){
29394         this.el.dom.style.zoom = 1;
29395     }
29396     Roo.Resizable.superclass.constructor.call(this);
29397 };
29398
29399 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29400         resizeChild : false,
29401         adjustments : [0, 0],
29402         minWidth : 5,
29403         minHeight : 5,
29404         maxWidth : 10000,
29405         maxHeight : 10000,
29406         enabled : true,
29407         animate : false,
29408         duration : .35,
29409         dynamic : false,
29410         handles : false,
29411         multiDirectional : false,
29412         disableTrackOver : false,
29413         easing : 'easeOutStrong',
29414         widthIncrement : 0,
29415         heightIncrement : 0,
29416         pinned : false,
29417         width : null,
29418         height : null,
29419         preserveRatio : false,
29420         transparent: false,
29421         minX: 0,
29422         minY: 0,
29423         draggable: false,
29424
29425         /**
29426          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29427          */
29428         constrainTo: undefined,
29429         /**
29430          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29431          */
29432         resizeRegion: undefined,
29433
29434
29435     /**
29436      * Perform a manual resize
29437      * @param {Number} width
29438      * @param {Number} height
29439      */
29440     resizeTo : function(width, height){
29441         this.el.setSize(width, height);
29442         this.updateChildSize();
29443         this.fireEvent("resize", this, width, height, null);
29444     },
29445
29446     // private
29447     startSizing : function(e, handle){
29448         this.fireEvent("beforeresize", this, e);
29449         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29450
29451             if(!this.overlay){
29452                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29453                 this.overlay.unselectable();
29454                 this.overlay.enableDisplayMode("block");
29455                 this.overlay.on("mousemove", this.onMouseMove, this);
29456                 this.overlay.on("mouseup", this.onMouseUp, this);
29457             }
29458             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29459
29460             this.resizing = true;
29461             this.startBox = this.el.getBox();
29462             this.startPoint = e.getXY();
29463             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29464                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29465
29466             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29467             this.overlay.show();
29468
29469             if(this.constrainTo) {
29470                 var ct = Roo.get(this.constrainTo);
29471                 this.resizeRegion = ct.getRegion().adjust(
29472                     ct.getFrameWidth('t'),
29473                     ct.getFrameWidth('l'),
29474                     -ct.getFrameWidth('b'),
29475                     -ct.getFrameWidth('r')
29476                 );
29477             }
29478
29479             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29480             this.proxy.show();
29481             this.proxy.setBox(this.startBox);
29482             if(!this.dynamic){
29483                 this.proxy.setStyle('visibility', 'visible');
29484             }
29485         }
29486     },
29487
29488     // private
29489     onMouseDown : function(handle, e){
29490         if(this.enabled){
29491             e.stopEvent();
29492             this.activeHandle = handle;
29493             this.startSizing(e, handle);
29494         }
29495     },
29496
29497     // private
29498     onMouseUp : function(e){
29499         var size = this.resizeElement();
29500         this.resizing = false;
29501         this.handleOut();
29502         this.overlay.hide();
29503         this.proxy.hide();
29504         this.fireEvent("resize", this, size.width, size.height, e);
29505     },
29506
29507     // private
29508     updateChildSize : function(){
29509         
29510         if(this.resizeChild){
29511             var el = this.el;
29512             var child = this.resizeChild;
29513             var adj = this.adjustments;
29514             if(el.dom.offsetWidth){
29515                 var b = el.getSize(true);
29516                 child.setSize(b.width+adj[0], b.height+adj[1]);
29517             }
29518             // Second call here for IE
29519             // The first call enables instant resizing and
29520             // the second call corrects scroll bars if they
29521             // exist
29522             if(Roo.isIE){
29523                 setTimeout(function(){
29524                     if(el.dom.offsetWidth){
29525                         var b = el.getSize(true);
29526                         child.setSize(b.width+adj[0], b.height+adj[1]);
29527                     }
29528                 }, 10);
29529             }
29530         }
29531     },
29532
29533     // private
29534     snap : function(value, inc, min){
29535         if(!inc || !value) return value;
29536         var newValue = value;
29537         var m = value % inc;
29538         if(m > 0){
29539             if(m > (inc/2)){
29540                 newValue = value + (inc-m);
29541             }else{
29542                 newValue = value - m;
29543             }
29544         }
29545         return Math.max(min, newValue);
29546     },
29547
29548     // private
29549     resizeElement : function(){
29550         var box = this.proxy.getBox();
29551         if(this.updateBox){
29552             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29553         }else{
29554             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29555         }
29556         this.updateChildSize();
29557         if(!this.dynamic){
29558             this.proxy.hide();
29559         }
29560         return box;
29561     },
29562
29563     // private
29564     constrain : function(v, diff, m, mx){
29565         if(v - diff < m){
29566             diff = v - m;
29567         }else if(v - diff > mx){
29568             diff = mx - v;
29569         }
29570         return diff;
29571     },
29572
29573     // private
29574     onMouseMove : function(e){
29575         
29576         if(this.enabled){
29577             try{// try catch so if something goes wrong the user doesn't get hung
29578
29579             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29580                 return;
29581             }
29582
29583             //var curXY = this.startPoint;
29584             var curSize = this.curSize || this.startBox;
29585             var x = this.startBox.x, y = this.startBox.y;
29586             var ox = x, oy = y;
29587             var w = curSize.width, h = curSize.height;
29588             var ow = w, oh = h;
29589             var mw = this.minWidth, mh = this.minHeight;
29590             var mxw = this.maxWidth, mxh = this.maxHeight;
29591             var wi = this.widthIncrement;
29592             var hi = this.heightIncrement;
29593
29594             var eventXY = e.getXY();
29595             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29596             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29597
29598             var pos = this.activeHandle.position;
29599
29600             switch(pos){
29601                 case "east":
29602                     w += diffX;
29603                     w = Math.min(Math.max(mw, w), mxw);
29604                     break;
29605              
29606                 case "south":
29607                     h += diffY;
29608                     h = Math.min(Math.max(mh, h), mxh);
29609                     break;
29610                 case "southeast":
29611                     w += diffX;
29612                     h += diffY;
29613                     w = Math.min(Math.max(mw, w), mxw);
29614                     h = Math.min(Math.max(mh, h), mxh);
29615                     break;
29616                 case "north":
29617                     diffY = this.constrain(h, diffY, mh, mxh);
29618                     y += diffY;
29619                     h -= diffY;
29620                     break;
29621                 case "hdrag":
29622                     
29623                     if (wi) {
29624                         var adiffX = Math.abs(diffX);
29625                         var sub = (adiffX % wi); // how much 
29626                         if (sub > (wi/2)) { // far enough to snap
29627                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29628                         } else {
29629                             // remove difference.. 
29630                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29631                         }
29632                     }
29633                     x += diffX;
29634                     x = Math.max(this.minX, x);
29635                     break;
29636                 case "west":
29637                     diffX = this.constrain(w, diffX, mw, mxw);
29638                     x += diffX;
29639                     w -= diffX;
29640                     break;
29641                 case "northeast":
29642                     w += diffX;
29643                     w = Math.min(Math.max(mw, w), mxw);
29644                     diffY = this.constrain(h, diffY, mh, mxh);
29645                     y += diffY;
29646                     h -= diffY;
29647                     break;
29648                 case "northwest":
29649                     diffX = this.constrain(w, diffX, mw, mxw);
29650                     diffY = this.constrain(h, diffY, mh, mxh);
29651                     y += diffY;
29652                     h -= diffY;
29653                     x += diffX;
29654                     w -= diffX;
29655                     break;
29656                case "southwest":
29657                     diffX = this.constrain(w, diffX, mw, mxw);
29658                     h += diffY;
29659                     h = Math.min(Math.max(mh, h), mxh);
29660                     x += diffX;
29661                     w -= diffX;
29662                     break;
29663             }
29664
29665             var sw = this.snap(w, wi, mw);
29666             var sh = this.snap(h, hi, mh);
29667             if(sw != w || sh != h){
29668                 switch(pos){
29669                     case "northeast":
29670                         y -= sh - h;
29671                     break;
29672                     case "north":
29673                         y -= sh - h;
29674                         break;
29675                     case "southwest":
29676                         x -= sw - w;
29677                     break;
29678                     case "west":
29679                         x -= sw - w;
29680                         break;
29681                     case "northwest":
29682                         x -= sw - w;
29683                         y -= sh - h;
29684                     break;
29685                 }
29686                 w = sw;
29687                 h = sh;
29688             }
29689
29690             if(this.preserveRatio){
29691                 switch(pos){
29692                     case "southeast":
29693                     case "east":
29694                         h = oh * (w/ow);
29695                         h = Math.min(Math.max(mh, h), mxh);
29696                         w = ow * (h/oh);
29697                        break;
29698                     case "south":
29699                         w = ow * (h/oh);
29700                         w = Math.min(Math.max(mw, w), mxw);
29701                         h = oh * (w/ow);
29702                         break;
29703                     case "northeast":
29704                         w = ow * (h/oh);
29705                         w = Math.min(Math.max(mw, w), mxw);
29706                         h = oh * (w/ow);
29707                     break;
29708                     case "north":
29709                         var tw = w;
29710                         w = ow * (h/oh);
29711                         w = Math.min(Math.max(mw, w), mxw);
29712                         h = oh * (w/ow);
29713                         x += (tw - w) / 2;
29714                         break;
29715                     case "southwest":
29716                         h = oh * (w/ow);
29717                         h = Math.min(Math.max(mh, h), mxh);
29718                         var tw = w;
29719                         w = ow * (h/oh);
29720                         x += tw - w;
29721                         break;
29722                     case "west":
29723                         var th = h;
29724                         h = oh * (w/ow);
29725                         h = Math.min(Math.max(mh, h), mxh);
29726                         y += (th - h) / 2;
29727                         var tw = w;
29728                         w = ow * (h/oh);
29729                         x += tw - w;
29730                        break;
29731                     case "northwest":
29732                         var tw = w;
29733                         var th = h;
29734                         h = oh * (w/ow);
29735                         h = Math.min(Math.max(mh, h), mxh);
29736                         w = ow * (h/oh);
29737                         y += th - h;
29738                         x += tw - w;
29739                        break;
29740
29741                 }
29742             }
29743             if (pos == 'hdrag') {
29744                 w = ow;
29745             }
29746             this.proxy.setBounds(x, y, w, h);
29747             if(this.dynamic){
29748                 this.resizeElement();
29749             }
29750             }catch(e){}
29751         }
29752         this.fireEvent("resizing", this, x, y, w, h, e);
29753     },
29754
29755     // private
29756     handleOver : function(){
29757         if(this.enabled){
29758             this.el.addClass("x-resizable-over");
29759         }
29760     },
29761
29762     // private
29763     handleOut : function(){
29764         if(!this.resizing){
29765             this.el.removeClass("x-resizable-over");
29766         }
29767     },
29768
29769     /**
29770      * Returns the element this component is bound to.
29771      * @return {Roo.Element}
29772      */
29773     getEl : function(){
29774         return this.el;
29775     },
29776
29777     /**
29778      * Returns the resizeChild element (or null).
29779      * @return {Roo.Element}
29780      */
29781     getResizeChild : function(){
29782         return this.resizeChild;
29783     },
29784     groupHandler : function()
29785     {
29786         
29787     },
29788     /**
29789      * Destroys this resizable. If the element was wrapped and
29790      * removeEl is not true then the element remains.
29791      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29792      */
29793     destroy : function(removeEl){
29794         this.proxy.remove();
29795         if(this.overlay){
29796             this.overlay.removeAllListeners();
29797             this.overlay.remove();
29798         }
29799         var ps = Roo.Resizable.positions;
29800         for(var k in ps){
29801             if(typeof ps[k] != "function" && this[ps[k]]){
29802                 var h = this[ps[k]];
29803                 h.el.removeAllListeners();
29804                 h.el.remove();
29805             }
29806         }
29807         if(removeEl){
29808             this.el.update("");
29809             this.el.remove();
29810         }
29811     }
29812 });
29813
29814 // private
29815 // hash to map config positions to true positions
29816 Roo.Resizable.positions = {
29817     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29818     hd: "hdrag"
29819 };
29820
29821 // private
29822 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29823     if(!this.tpl){
29824         // only initialize the template if resizable is used
29825         var tpl = Roo.DomHelper.createTemplate(
29826             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29827         );
29828         tpl.compile();
29829         Roo.Resizable.Handle.prototype.tpl = tpl;
29830     }
29831     this.position = pos;
29832     this.rz = rz;
29833     // show north drag fro topdra
29834     var handlepos = pos == 'hdrag' ? 'north' : pos;
29835     
29836     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29837     if (pos == 'hdrag') {
29838         this.el.setStyle('cursor', 'pointer');
29839     }
29840     this.el.unselectable();
29841     if(transparent){
29842         this.el.setOpacity(0);
29843     }
29844     this.el.on("mousedown", this.onMouseDown, this);
29845     if(!disableTrackOver){
29846         this.el.on("mouseover", this.onMouseOver, this);
29847         this.el.on("mouseout", this.onMouseOut, this);
29848     }
29849 };
29850
29851 // private
29852 Roo.Resizable.Handle.prototype = {
29853     afterResize : function(rz){
29854         Roo.log('after?');
29855         // do nothing
29856     },
29857     // private
29858     onMouseDown : function(e){
29859         this.rz.onMouseDown(this, e);
29860     },
29861     // private
29862     onMouseOver : function(e){
29863         this.rz.handleOver(this, e);
29864     },
29865     // private
29866     onMouseOut : function(e){
29867         this.rz.handleOut(this, e);
29868     }
29869 };/*
29870  * Based on:
29871  * Ext JS Library 1.1.1
29872  * Copyright(c) 2006-2007, Ext JS, LLC.
29873  *
29874  * Originally Released Under LGPL - original licence link has changed is not relivant.
29875  *
29876  * Fork - LGPL
29877  * <script type="text/javascript">
29878  */
29879
29880 /**
29881  * @class Roo.Editor
29882  * @extends Roo.Component
29883  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29884  * @constructor
29885  * Create a new Editor
29886  * @param {Roo.form.Field} field The Field object (or descendant)
29887  * @param {Object} config The config object
29888  */
29889 Roo.Editor = function(field, config){
29890     Roo.Editor.superclass.constructor.call(this, config);
29891     this.field = field;
29892     this.addEvents({
29893         /**
29894              * @event beforestartedit
29895              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29896              * false from the handler of this event.
29897              * @param {Editor} this
29898              * @param {Roo.Element} boundEl The underlying element bound to this editor
29899              * @param {Mixed} value The field value being set
29900              */
29901         "beforestartedit" : true,
29902         /**
29903              * @event startedit
29904              * Fires when this editor is displayed
29905              * @param {Roo.Element} boundEl The underlying element bound to this editor
29906              * @param {Mixed} value The starting field value
29907              */
29908         "startedit" : true,
29909         /**
29910              * @event beforecomplete
29911              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29912              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29913              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29914              * event will not fire since no edit actually occurred.
29915              * @param {Editor} this
29916              * @param {Mixed} value The current field value
29917              * @param {Mixed} startValue The original field value
29918              */
29919         "beforecomplete" : true,
29920         /**
29921              * @event complete
29922              * Fires after editing is complete and any changed value has been written to the underlying field.
29923              * @param {Editor} this
29924              * @param {Mixed} value The current field value
29925              * @param {Mixed} startValue The original field value
29926              */
29927         "complete" : true,
29928         /**
29929          * @event specialkey
29930          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29931          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29932          * @param {Roo.form.Field} this
29933          * @param {Roo.EventObject} e The event object
29934          */
29935         "specialkey" : true
29936     });
29937 };
29938
29939 Roo.extend(Roo.Editor, Roo.Component, {
29940     /**
29941      * @cfg {Boolean/String} autosize
29942      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29943      * or "height" to adopt the height only (defaults to false)
29944      */
29945     /**
29946      * @cfg {Boolean} revertInvalid
29947      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29948      * validation fails (defaults to true)
29949      */
29950     /**
29951      * @cfg {Boolean} ignoreNoChange
29952      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29953      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29954      * will never be ignored.
29955      */
29956     /**
29957      * @cfg {Boolean} hideEl
29958      * False to keep the bound element visible while the editor is displayed (defaults to true)
29959      */
29960     /**
29961      * @cfg {Mixed} value
29962      * The data value of the underlying field (defaults to "")
29963      */
29964     value : "",
29965     /**
29966      * @cfg {String} alignment
29967      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29968      */
29969     alignment: "c-c?",
29970     /**
29971      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29972      * for bottom-right shadow (defaults to "frame")
29973      */
29974     shadow : "frame",
29975     /**
29976      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29977      */
29978     constrain : false,
29979     /**
29980      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29981      */
29982     completeOnEnter : false,
29983     /**
29984      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29985      */
29986     cancelOnEsc : false,
29987     /**
29988      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29989      */
29990     updateEl : false,
29991
29992     // private
29993     onRender : function(ct, position){
29994         this.el = new Roo.Layer({
29995             shadow: this.shadow,
29996             cls: "x-editor",
29997             parentEl : ct,
29998             shim : this.shim,
29999             shadowOffset:4,
30000             id: this.id,
30001             constrain: this.constrain
30002         });
30003         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
30004         if(this.field.msgTarget != 'title'){
30005             this.field.msgTarget = 'qtip';
30006         }
30007         this.field.render(this.el);
30008         if(Roo.isGecko){
30009             this.field.el.dom.setAttribute('autocomplete', 'off');
30010         }
30011         this.field.on("specialkey", this.onSpecialKey, this);
30012         if(this.swallowKeys){
30013             this.field.el.swallowEvent(['keydown','keypress']);
30014         }
30015         this.field.show();
30016         this.field.on("blur", this.onBlur, this);
30017         if(this.field.grow){
30018             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
30019         }
30020     },
30021
30022     onSpecialKey : function(field, e)
30023     {
30024         //Roo.log('editor onSpecialKey');
30025         if(this.completeOnEnter && e.getKey() == e.ENTER){
30026             e.stopEvent();
30027             this.completeEdit();
30028             return;
30029         }
30030         // do not fire special key otherwise it might hide close the editor...
30031         if(e.getKey() == e.ENTER){    
30032             return;
30033         }
30034         if(this.cancelOnEsc && e.getKey() == e.ESC){
30035             this.cancelEdit();
30036             return;
30037         } 
30038         this.fireEvent('specialkey', field, e);
30039     
30040     },
30041
30042     /**
30043      * Starts the editing process and shows the editor.
30044      * @param {String/HTMLElement/Element} el The element to edit
30045      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30046       * to the innerHTML of el.
30047      */
30048     startEdit : function(el, value){
30049         if(this.editing){
30050             this.completeEdit();
30051         }
30052         this.boundEl = Roo.get(el);
30053         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30054         if(!this.rendered){
30055             this.render(this.parentEl || document.body);
30056         }
30057         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30058             return;
30059         }
30060         this.startValue = v;
30061         this.field.setValue(v);
30062         if(this.autoSize){
30063             var sz = this.boundEl.getSize();
30064             switch(this.autoSize){
30065                 case "width":
30066                 this.setSize(sz.width,  "");
30067                 break;
30068                 case "height":
30069                 this.setSize("",  sz.height);
30070                 break;
30071                 default:
30072                 this.setSize(sz.width,  sz.height);
30073             }
30074         }
30075         this.el.alignTo(this.boundEl, this.alignment);
30076         this.editing = true;
30077         if(Roo.QuickTips){
30078             Roo.QuickTips.disable();
30079         }
30080         this.show();
30081     },
30082
30083     /**
30084      * Sets the height and width of this editor.
30085      * @param {Number} width The new width
30086      * @param {Number} height The new height
30087      */
30088     setSize : function(w, h){
30089         this.field.setSize(w, h);
30090         if(this.el){
30091             this.el.sync();
30092         }
30093     },
30094
30095     /**
30096      * Realigns the editor to the bound field based on the current alignment config value.
30097      */
30098     realign : function(){
30099         this.el.alignTo(this.boundEl, this.alignment);
30100     },
30101
30102     /**
30103      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30104      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30105      */
30106     completeEdit : function(remainVisible){
30107         if(!this.editing){
30108             return;
30109         }
30110         var v = this.getValue();
30111         if(this.revertInvalid !== false && !this.field.isValid()){
30112             v = this.startValue;
30113             this.cancelEdit(true);
30114         }
30115         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30116             this.editing = false;
30117             this.hide();
30118             return;
30119         }
30120         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30121             this.editing = false;
30122             if(this.updateEl && this.boundEl){
30123                 this.boundEl.update(v);
30124             }
30125             if(remainVisible !== true){
30126                 this.hide();
30127             }
30128             this.fireEvent("complete", this, v, this.startValue);
30129         }
30130     },
30131
30132     // private
30133     onShow : function(){
30134         this.el.show();
30135         if(this.hideEl !== false){
30136             this.boundEl.hide();
30137         }
30138         this.field.show();
30139         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30140             this.fixIEFocus = true;
30141             this.deferredFocus.defer(50, this);
30142         }else{
30143             this.field.focus();
30144         }
30145         this.fireEvent("startedit", this.boundEl, this.startValue);
30146     },
30147
30148     deferredFocus : function(){
30149         if(this.editing){
30150             this.field.focus();
30151         }
30152     },
30153
30154     /**
30155      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30156      * reverted to the original starting value.
30157      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30158      * cancel (defaults to false)
30159      */
30160     cancelEdit : function(remainVisible){
30161         if(this.editing){
30162             this.setValue(this.startValue);
30163             if(remainVisible !== true){
30164                 this.hide();
30165             }
30166         }
30167     },
30168
30169     // private
30170     onBlur : function(){
30171         if(this.allowBlur !== true && this.editing){
30172             this.completeEdit();
30173         }
30174     },
30175
30176     // private
30177     onHide : function(){
30178         if(this.editing){
30179             this.completeEdit();
30180             return;
30181         }
30182         this.field.blur();
30183         if(this.field.collapse){
30184             this.field.collapse();
30185         }
30186         this.el.hide();
30187         if(this.hideEl !== false){
30188             this.boundEl.show();
30189         }
30190         if(Roo.QuickTips){
30191             Roo.QuickTips.enable();
30192         }
30193     },
30194
30195     /**
30196      * Sets the data value of the editor
30197      * @param {Mixed} value Any valid value supported by the underlying field
30198      */
30199     setValue : function(v){
30200         this.field.setValue(v);
30201     },
30202
30203     /**
30204      * Gets the data value of the editor
30205      * @return {Mixed} The data value
30206      */
30207     getValue : function(){
30208         return this.field.getValue();
30209     }
30210 });/*
30211  * Based on:
30212  * Ext JS Library 1.1.1
30213  * Copyright(c) 2006-2007, Ext JS, LLC.
30214  *
30215  * Originally Released Under LGPL - original licence link has changed is not relivant.
30216  *
30217  * Fork - LGPL
30218  * <script type="text/javascript">
30219  */
30220  
30221 /**
30222  * @class Roo.BasicDialog
30223  * @extends Roo.util.Observable
30224  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30225  * <pre><code>
30226 var dlg = new Roo.BasicDialog("my-dlg", {
30227     height: 200,
30228     width: 300,
30229     minHeight: 100,
30230     minWidth: 150,
30231     modal: true,
30232     proxyDrag: true,
30233     shadow: true
30234 });
30235 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30236 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30237 dlg.addButton('Cancel', dlg.hide, dlg);
30238 dlg.show();
30239 </code></pre>
30240   <b>A Dialog should always be a direct child of the body element.</b>
30241  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30242  * @cfg {String} title Default text to display in the title bar (defaults to null)
30243  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30244  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30245  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30246  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30247  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30248  * (defaults to null with no animation)
30249  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30250  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30251  * property for valid values (defaults to 'all')
30252  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30253  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30254  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30255  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30256  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30257  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30258  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30259  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30260  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30261  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30262  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30263  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30264  * draggable = true (defaults to false)
30265  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30266  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30267  * shadow (defaults to false)
30268  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30269  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30270  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30271  * @cfg {Array} buttons Array of buttons
30272  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30273  * @constructor
30274  * Create a new BasicDialog.
30275  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30276  * @param {Object} config Configuration options
30277  */
30278 Roo.BasicDialog = function(el, config){
30279     this.el = Roo.get(el);
30280     var dh = Roo.DomHelper;
30281     if(!this.el && config && config.autoCreate){
30282         if(typeof config.autoCreate == "object"){
30283             if(!config.autoCreate.id){
30284                 config.autoCreate.id = el;
30285             }
30286             this.el = dh.append(document.body,
30287                         config.autoCreate, true);
30288         }else{
30289             this.el = dh.append(document.body,
30290                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30291         }
30292     }
30293     el = this.el;
30294     el.setDisplayed(true);
30295     el.hide = this.hideAction;
30296     this.id = el.id;
30297     el.addClass("x-dlg");
30298
30299     Roo.apply(this, config);
30300
30301     this.proxy = el.createProxy("x-dlg-proxy");
30302     this.proxy.hide = this.hideAction;
30303     this.proxy.setOpacity(.5);
30304     this.proxy.hide();
30305
30306     if(config.width){
30307         el.setWidth(config.width);
30308     }
30309     if(config.height){
30310         el.setHeight(config.height);
30311     }
30312     this.size = el.getSize();
30313     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30314         this.xy = [config.x,config.y];
30315     }else{
30316         this.xy = el.getCenterXY(true);
30317     }
30318     /** The header element @type Roo.Element */
30319     this.header = el.child("> .x-dlg-hd");
30320     /** The body element @type Roo.Element */
30321     this.body = el.child("> .x-dlg-bd");
30322     /** The footer element @type Roo.Element */
30323     this.footer = el.child("> .x-dlg-ft");
30324
30325     if(!this.header){
30326         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30327     }
30328     if(!this.body){
30329         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30330     }
30331
30332     this.header.unselectable();
30333     if(this.title){
30334         this.header.update(this.title);
30335     }
30336     // this element allows the dialog to be focused for keyboard event
30337     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30338     this.focusEl.swallowEvent("click", true);
30339
30340     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30341
30342     // wrap the body and footer for special rendering
30343     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30344     if(this.footer){
30345         this.bwrap.dom.appendChild(this.footer.dom);
30346     }
30347
30348     this.bg = this.el.createChild({
30349         tag: "div", cls:"x-dlg-bg",
30350         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30351     });
30352     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30353
30354
30355     if(this.autoScroll !== false && !this.autoTabs){
30356         this.body.setStyle("overflow", "auto");
30357     }
30358
30359     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30360
30361     if(this.closable !== false){
30362         this.el.addClass("x-dlg-closable");
30363         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30364         this.close.on("click", this.closeClick, this);
30365         this.close.addClassOnOver("x-dlg-close-over");
30366     }
30367     if(this.collapsible !== false){
30368         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30369         this.collapseBtn.on("click", this.collapseClick, this);
30370         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30371         this.header.on("dblclick", this.collapseClick, this);
30372     }
30373     if(this.resizable !== false){
30374         this.el.addClass("x-dlg-resizable");
30375         this.resizer = new Roo.Resizable(el, {
30376             minWidth: this.minWidth || 80,
30377             minHeight:this.minHeight || 80,
30378             handles: this.resizeHandles || "all",
30379             pinned: true
30380         });
30381         this.resizer.on("beforeresize", this.beforeResize, this);
30382         this.resizer.on("resize", this.onResize, this);
30383     }
30384     if(this.draggable !== false){
30385         el.addClass("x-dlg-draggable");
30386         if (!this.proxyDrag) {
30387             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30388         }
30389         else {
30390             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30391         }
30392         dd.setHandleElId(this.header.id);
30393         dd.endDrag = this.endMove.createDelegate(this);
30394         dd.startDrag = this.startMove.createDelegate(this);
30395         dd.onDrag = this.onDrag.createDelegate(this);
30396         dd.scroll = false;
30397         this.dd = dd;
30398     }
30399     if(this.modal){
30400         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30401         this.mask.enableDisplayMode("block");
30402         this.mask.hide();
30403         this.el.addClass("x-dlg-modal");
30404     }
30405     if(this.shadow){
30406         this.shadow = new Roo.Shadow({
30407             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30408             offset : this.shadowOffset
30409         });
30410     }else{
30411         this.shadowOffset = 0;
30412     }
30413     if(Roo.useShims && this.shim !== false){
30414         this.shim = this.el.createShim();
30415         this.shim.hide = this.hideAction;
30416         this.shim.hide();
30417     }else{
30418         this.shim = false;
30419     }
30420     if(this.autoTabs){
30421         this.initTabs();
30422     }
30423     if (this.buttons) { 
30424         var bts= this.buttons;
30425         this.buttons = [];
30426         Roo.each(bts, function(b) {
30427             this.addButton(b);
30428         }, this);
30429     }
30430     
30431     
30432     this.addEvents({
30433         /**
30434          * @event keydown
30435          * Fires when a key is pressed
30436          * @param {Roo.BasicDialog} this
30437          * @param {Roo.EventObject} e
30438          */
30439         "keydown" : true,
30440         /**
30441          * @event move
30442          * Fires when this dialog is moved by the user.
30443          * @param {Roo.BasicDialog} this
30444          * @param {Number} x The new page X
30445          * @param {Number} y The new page Y
30446          */
30447         "move" : true,
30448         /**
30449          * @event resize
30450          * Fires when this dialog is resized by the user.
30451          * @param {Roo.BasicDialog} this
30452          * @param {Number} width The new width
30453          * @param {Number} height The new height
30454          */
30455         "resize" : true,
30456         /**
30457          * @event beforehide
30458          * Fires before this dialog is hidden.
30459          * @param {Roo.BasicDialog} this
30460          */
30461         "beforehide" : true,
30462         /**
30463          * @event hide
30464          * Fires when this dialog is hidden.
30465          * @param {Roo.BasicDialog} this
30466          */
30467         "hide" : true,
30468         /**
30469          * @event beforeshow
30470          * Fires before this dialog is shown.
30471          * @param {Roo.BasicDialog} this
30472          */
30473         "beforeshow" : true,
30474         /**
30475          * @event show
30476          * Fires when this dialog is shown.
30477          * @param {Roo.BasicDialog} this
30478          */
30479         "show" : true
30480     });
30481     el.on("keydown", this.onKeyDown, this);
30482     el.on("mousedown", this.toFront, this);
30483     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30484     this.el.hide();
30485     Roo.DialogManager.register(this);
30486     Roo.BasicDialog.superclass.constructor.call(this);
30487 };
30488
30489 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30490     shadowOffset: Roo.isIE ? 6 : 5,
30491     minHeight: 80,
30492     minWidth: 200,
30493     minButtonWidth: 75,
30494     defaultButton: null,
30495     buttonAlign: "right",
30496     tabTag: 'div',
30497     firstShow: true,
30498
30499     /**
30500      * Sets the dialog title text
30501      * @param {String} text The title text to display
30502      * @return {Roo.BasicDialog} this
30503      */
30504     setTitle : function(text){
30505         this.header.update(text);
30506         return this;
30507     },
30508
30509     // private
30510     closeClick : function(){
30511         this.hide();
30512     },
30513
30514     // private
30515     collapseClick : function(){
30516         this[this.collapsed ? "expand" : "collapse"]();
30517     },
30518
30519     /**
30520      * Collapses the dialog to its minimized state (only the title bar is visible).
30521      * Equivalent to the user clicking the collapse dialog button.
30522      */
30523     collapse : function(){
30524         if(!this.collapsed){
30525             this.collapsed = true;
30526             this.el.addClass("x-dlg-collapsed");
30527             this.restoreHeight = this.el.getHeight();
30528             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30529         }
30530     },
30531
30532     /**
30533      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30534      * clicking the expand dialog button.
30535      */
30536     expand : function(){
30537         if(this.collapsed){
30538             this.collapsed = false;
30539             this.el.removeClass("x-dlg-collapsed");
30540             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30541         }
30542     },
30543
30544     /**
30545      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30546      * @return {Roo.TabPanel} The tabs component
30547      */
30548     initTabs : function(){
30549         var tabs = this.getTabs();
30550         while(tabs.getTab(0)){
30551             tabs.removeTab(0);
30552         }
30553         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30554             var dom = el.dom;
30555             tabs.addTab(Roo.id(dom), dom.title);
30556             dom.title = "";
30557         });
30558         tabs.activate(0);
30559         return tabs;
30560     },
30561
30562     // private
30563     beforeResize : function(){
30564         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30565     },
30566
30567     // private
30568     onResize : function(){
30569         this.refreshSize();
30570         this.syncBodyHeight();
30571         this.adjustAssets();
30572         this.focus();
30573         this.fireEvent("resize", this, this.size.width, this.size.height);
30574     },
30575
30576     // private
30577     onKeyDown : function(e){
30578         if(this.isVisible()){
30579             this.fireEvent("keydown", this, e);
30580         }
30581     },
30582
30583     /**
30584      * Resizes the dialog.
30585      * @param {Number} width
30586      * @param {Number} height
30587      * @return {Roo.BasicDialog} this
30588      */
30589     resizeTo : function(width, height){
30590         this.el.setSize(width, height);
30591         this.size = {width: width, height: height};
30592         this.syncBodyHeight();
30593         if(this.fixedcenter){
30594             this.center();
30595         }
30596         if(this.isVisible()){
30597             this.constrainXY();
30598             this.adjustAssets();
30599         }
30600         this.fireEvent("resize", this, width, height);
30601         return this;
30602     },
30603
30604
30605     /**
30606      * Resizes the dialog to fit the specified content size.
30607      * @param {Number} width
30608      * @param {Number} height
30609      * @return {Roo.BasicDialog} this
30610      */
30611     setContentSize : function(w, h){
30612         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30613         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30614         //if(!this.el.isBorderBox()){
30615             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30616             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30617         //}
30618         if(this.tabs){
30619             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30620             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30621         }
30622         this.resizeTo(w, h);
30623         return this;
30624     },
30625
30626     /**
30627      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30628      * executed in response to a particular key being pressed while the dialog is active.
30629      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30630      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30631      * @param {Function} fn The function to call
30632      * @param {Object} scope (optional) The scope of the function
30633      * @return {Roo.BasicDialog} this
30634      */
30635     addKeyListener : function(key, fn, scope){
30636         var keyCode, shift, ctrl, alt;
30637         if(typeof key == "object" && !(key instanceof Array)){
30638             keyCode = key["key"];
30639             shift = key["shift"];
30640             ctrl = key["ctrl"];
30641             alt = key["alt"];
30642         }else{
30643             keyCode = key;
30644         }
30645         var handler = function(dlg, e){
30646             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30647                 var k = e.getKey();
30648                 if(keyCode instanceof Array){
30649                     for(var i = 0, len = keyCode.length; i < len; i++){
30650                         if(keyCode[i] == k){
30651                           fn.call(scope || window, dlg, k, e);
30652                           return;
30653                         }
30654                     }
30655                 }else{
30656                     if(k == keyCode){
30657                         fn.call(scope || window, dlg, k, e);
30658                     }
30659                 }
30660             }
30661         };
30662         this.on("keydown", handler);
30663         return this;
30664     },
30665
30666     /**
30667      * Returns the TabPanel component (creates it if it doesn't exist).
30668      * Note: If you wish to simply check for the existence of tabs without creating them,
30669      * check for a null 'tabs' property.
30670      * @return {Roo.TabPanel} The tabs component
30671      */
30672     getTabs : function(){
30673         if(!this.tabs){
30674             this.el.addClass("x-dlg-auto-tabs");
30675             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30676             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30677         }
30678         return this.tabs;
30679     },
30680
30681     /**
30682      * Adds a button to the footer section of the dialog.
30683      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30684      * object or a valid Roo.DomHelper element config
30685      * @param {Function} handler The function called when the button is clicked
30686      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30687      * @return {Roo.Button} The new button
30688      */
30689     addButton : function(config, handler, scope){
30690         var dh = Roo.DomHelper;
30691         if(!this.footer){
30692             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30693         }
30694         if(!this.btnContainer){
30695             var tb = this.footer.createChild({
30696
30697                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30698                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30699             }, null, true);
30700             this.btnContainer = tb.firstChild.firstChild.firstChild;
30701         }
30702         var bconfig = {
30703             handler: handler,
30704             scope: scope,
30705             minWidth: this.minButtonWidth,
30706             hideParent:true
30707         };
30708         if(typeof config == "string"){
30709             bconfig.text = config;
30710         }else{
30711             if(config.tag){
30712                 bconfig.dhconfig = config;
30713             }else{
30714                 Roo.apply(bconfig, config);
30715             }
30716         }
30717         var fc = false;
30718         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30719             bconfig.position = Math.max(0, bconfig.position);
30720             fc = this.btnContainer.childNodes[bconfig.position];
30721         }
30722          
30723         var btn = new Roo.Button(
30724             fc ? 
30725                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30726                 : this.btnContainer.appendChild(document.createElement("td")),
30727             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30728             bconfig
30729         );
30730         this.syncBodyHeight();
30731         if(!this.buttons){
30732             /**
30733              * Array of all the buttons that have been added to this dialog via addButton
30734              * @type Array
30735              */
30736             this.buttons = [];
30737         }
30738         this.buttons.push(btn);
30739         return btn;
30740     },
30741
30742     /**
30743      * Sets the default button to be focused when the dialog is displayed.
30744      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30745      * @return {Roo.BasicDialog} this
30746      */
30747     setDefaultButton : function(btn){
30748         this.defaultButton = btn;
30749         return this;
30750     },
30751
30752     // private
30753     getHeaderFooterHeight : function(safe){
30754         var height = 0;
30755         if(this.header){
30756            height += this.header.getHeight();
30757         }
30758         if(this.footer){
30759            var fm = this.footer.getMargins();
30760             height += (this.footer.getHeight()+fm.top+fm.bottom);
30761         }
30762         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30763         height += this.centerBg.getPadding("tb");
30764         return height;
30765     },
30766
30767     // private
30768     syncBodyHeight : function()
30769     {
30770         var bd = this.body, // the text
30771             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30772             bw = this.bwrap;
30773         var height = this.size.height - this.getHeaderFooterHeight(false);
30774         bd.setHeight(height-bd.getMargins("tb"));
30775         var hh = this.header.getHeight();
30776         var h = this.size.height-hh;
30777         cb.setHeight(h);
30778         
30779         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30780         bw.setHeight(h-cb.getPadding("tb"));
30781         
30782         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30783         bd.setWidth(bw.getWidth(true));
30784         if(this.tabs){
30785             this.tabs.syncHeight();
30786             if(Roo.isIE){
30787                 this.tabs.el.repaint();
30788             }
30789         }
30790     },
30791
30792     /**
30793      * Restores the previous state of the dialog if Roo.state is configured.
30794      * @return {Roo.BasicDialog} this
30795      */
30796     restoreState : function(){
30797         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30798         if(box && box.width){
30799             this.xy = [box.x, box.y];
30800             this.resizeTo(box.width, box.height);
30801         }
30802         return this;
30803     },
30804
30805     // private
30806     beforeShow : function(){
30807         this.expand();
30808         if(this.fixedcenter){
30809             this.xy = this.el.getCenterXY(true);
30810         }
30811         if(this.modal){
30812             Roo.get(document.body).addClass("x-body-masked");
30813             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30814             this.mask.show();
30815         }
30816         this.constrainXY();
30817     },
30818
30819     // private
30820     animShow : function(){
30821         var b = Roo.get(this.animateTarget).getBox();
30822         this.proxy.setSize(b.width, b.height);
30823         this.proxy.setLocation(b.x, b.y);
30824         this.proxy.show();
30825         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30826                     true, .35, this.showEl.createDelegate(this));
30827     },
30828
30829     /**
30830      * Shows the dialog.
30831      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30832      * @return {Roo.BasicDialog} this
30833      */
30834     show : function(animateTarget){
30835         if (this.fireEvent("beforeshow", this) === false){
30836             return;
30837         }
30838         if(this.syncHeightBeforeShow){
30839             this.syncBodyHeight();
30840         }else if(this.firstShow){
30841             this.firstShow = false;
30842             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30843         }
30844         this.animateTarget = animateTarget || this.animateTarget;
30845         if(!this.el.isVisible()){
30846             this.beforeShow();
30847             if(this.animateTarget && Roo.get(this.animateTarget)){
30848                 this.animShow();
30849             }else{
30850                 this.showEl();
30851             }
30852         }
30853         return this;
30854     },
30855
30856     // private
30857     showEl : function(){
30858         this.proxy.hide();
30859         this.el.setXY(this.xy);
30860         this.el.show();
30861         this.adjustAssets(true);
30862         this.toFront();
30863         this.focus();
30864         // IE peekaboo bug - fix found by Dave Fenwick
30865         if(Roo.isIE){
30866             this.el.repaint();
30867         }
30868         this.fireEvent("show", this);
30869     },
30870
30871     /**
30872      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30873      * dialog itself will receive focus.
30874      */
30875     focus : function(){
30876         if(this.defaultButton){
30877             this.defaultButton.focus();
30878         }else{
30879             this.focusEl.focus();
30880         }
30881     },
30882
30883     // private
30884     constrainXY : function(){
30885         if(this.constraintoviewport !== false){
30886             if(!this.viewSize){
30887                 if(this.container){
30888                     var s = this.container.getSize();
30889                     this.viewSize = [s.width, s.height];
30890                 }else{
30891                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30892                 }
30893             }
30894             var s = Roo.get(this.container||document).getScroll();
30895
30896             var x = this.xy[0], y = this.xy[1];
30897             var w = this.size.width, h = this.size.height;
30898             var vw = this.viewSize[0], vh = this.viewSize[1];
30899             // only move it if it needs it
30900             var moved = false;
30901             // first validate right/bottom
30902             if(x + w > vw+s.left){
30903                 x = vw - w;
30904                 moved = true;
30905             }
30906             if(y + h > vh+s.top){
30907                 y = vh - h;
30908                 moved = true;
30909             }
30910             // then make sure top/left isn't negative
30911             if(x < s.left){
30912                 x = s.left;
30913                 moved = true;
30914             }
30915             if(y < s.top){
30916                 y = s.top;
30917                 moved = true;
30918             }
30919             if(moved){
30920                 // cache xy
30921                 this.xy = [x, y];
30922                 if(this.isVisible()){
30923                     this.el.setLocation(x, y);
30924                     this.adjustAssets();
30925                 }
30926             }
30927         }
30928     },
30929
30930     // private
30931     onDrag : function(){
30932         if(!this.proxyDrag){
30933             this.xy = this.el.getXY();
30934             this.adjustAssets();
30935         }
30936     },
30937
30938     // private
30939     adjustAssets : function(doShow){
30940         var x = this.xy[0], y = this.xy[1];
30941         var w = this.size.width, h = this.size.height;
30942         if(doShow === true){
30943             if(this.shadow){
30944                 this.shadow.show(this.el);
30945             }
30946             if(this.shim){
30947                 this.shim.show();
30948             }
30949         }
30950         if(this.shadow && this.shadow.isVisible()){
30951             this.shadow.show(this.el);
30952         }
30953         if(this.shim && this.shim.isVisible()){
30954             this.shim.setBounds(x, y, w, h);
30955         }
30956     },
30957
30958     // private
30959     adjustViewport : function(w, h){
30960         if(!w || !h){
30961             w = Roo.lib.Dom.getViewWidth();
30962             h = Roo.lib.Dom.getViewHeight();
30963         }
30964         // cache the size
30965         this.viewSize = [w, h];
30966         if(this.modal && this.mask.isVisible()){
30967             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30968             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30969         }
30970         if(this.isVisible()){
30971             this.constrainXY();
30972         }
30973     },
30974
30975     /**
30976      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30977      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30978      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30979      */
30980     destroy : function(removeEl){
30981         if(this.isVisible()){
30982             this.animateTarget = null;
30983             this.hide();
30984         }
30985         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30986         if(this.tabs){
30987             this.tabs.destroy(removeEl);
30988         }
30989         Roo.destroy(
30990              this.shim,
30991              this.proxy,
30992              this.resizer,
30993              this.close,
30994              this.mask
30995         );
30996         if(this.dd){
30997             this.dd.unreg();
30998         }
30999         if(this.buttons){
31000            for(var i = 0, len = this.buttons.length; i < len; i++){
31001                this.buttons[i].destroy();
31002            }
31003         }
31004         this.el.removeAllListeners();
31005         if(removeEl === true){
31006             this.el.update("");
31007             this.el.remove();
31008         }
31009         Roo.DialogManager.unregister(this);
31010     },
31011
31012     // private
31013     startMove : function(){
31014         if(this.proxyDrag){
31015             this.proxy.show();
31016         }
31017         if(this.constraintoviewport !== false){
31018             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
31019         }
31020     },
31021
31022     // private
31023     endMove : function(){
31024         if(!this.proxyDrag){
31025             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
31026         }else{
31027             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
31028             this.proxy.hide();
31029         }
31030         this.refreshSize();
31031         this.adjustAssets();
31032         this.focus();
31033         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31034     },
31035
31036     /**
31037      * Brings this dialog to the front of any other visible dialogs
31038      * @return {Roo.BasicDialog} this
31039      */
31040     toFront : function(){
31041         Roo.DialogManager.bringToFront(this);
31042         return this;
31043     },
31044
31045     /**
31046      * Sends this dialog to the back (under) of any other visible dialogs
31047      * @return {Roo.BasicDialog} this
31048      */
31049     toBack : function(){
31050         Roo.DialogManager.sendToBack(this);
31051         return this;
31052     },
31053
31054     /**
31055      * Centers this dialog in the viewport
31056      * @return {Roo.BasicDialog} this
31057      */
31058     center : function(){
31059         var xy = this.el.getCenterXY(true);
31060         this.moveTo(xy[0], xy[1]);
31061         return this;
31062     },
31063
31064     /**
31065      * Moves the dialog's top-left corner to the specified point
31066      * @param {Number} x
31067      * @param {Number} y
31068      * @return {Roo.BasicDialog} this
31069      */
31070     moveTo : function(x, y){
31071         this.xy = [x,y];
31072         if(this.isVisible()){
31073             this.el.setXY(this.xy);
31074             this.adjustAssets();
31075         }
31076         return this;
31077     },
31078
31079     /**
31080      * Aligns the dialog to the specified element
31081      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31082      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31083      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31084      * @return {Roo.BasicDialog} this
31085      */
31086     alignTo : function(element, position, offsets){
31087         this.xy = this.el.getAlignToXY(element, position, offsets);
31088         if(this.isVisible()){
31089             this.el.setXY(this.xy);
31090             this.adjustAssets();
31091         }
31092         return this;
31093     },
31094
31095     /**
31096      * Anchors an element to another element and realigns it when the window is resized.
31097      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31098      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31099      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31100      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31101      * is a number, it is used as the buffer delay (defaults to 50ms).
31102      * @return {Roo.BasicDialog} this
31103      */
31104     anchorTo : function(el, alignment, offsets, monitorScroll){
31105         var action = function(){
31106             this.alignTo(el, alignment, offsets);
31107         };
31108         Roo.EventManager.onWindowResize(action, this);
31109         var tm = typeof monitorScroll;
31110         if(tm != 'undefined'){
31111             Roo.EventManager.on(window, 'scroll', action, this,
31112                 {buffer: tm == 'number' ? monitorScroll : 50});
31113         }
31114         action.call(this);
31115         return this;
31116     },
31117
31118     /**
31119      * Returns true if the dialog is visible
31120      * @return {Boolean}
31121      */
31122     isVisible : function(){
31123         return this.el.isVisible();
31124     },
31125
31126     // private
31127     animHide : function(callback){
31128         var b = Roo.get(this.animateTarget).getBox();
31129         this.proxy.show();
31130         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31131         this.el.hide();
31132         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31133                     this.hideEl.createDelegate(this, [callback]));
31134     },
31135
31136     /**
31137      * Hides the dialog.
31138      * @param {Function} callback (optional) Function to call when the dialog is hidden
31139      * @return {Roo.BasicDialog} this
31140      */
31141     hide : function(callback){
31142         if (this.fireEvent("beforehide", this) === false){
31143             return;
31144         }
31145         if(this.shadow){
31146             this.shadow.hide();
31147         }
31148         if(this.shim) {
31149           this.shim.hide();
31150         }
31151         // sometimes animateTarget seems to get set.. causing problems...
31152         // this just double checks..
31153         if(this.animateTarget && Roo.get(this.animateTarget)) {
31154            this.animHide(callback);
31155         }else{
31156             this.el.hide();
31157             this.hideEl(callback);
31158         }
31159         return this;
31160     },
31161
31162     // private
31163     hideEl : function(callback){
31164         this.proxy.hide();
31165         if(this.modal){
31166             this.mask.hide();
31167             Roo.get(document.body).removeClass("x-body-masked");
31168         }
31169         this.fireEvent("hide", this);
31170         if(typeof callback == "function"){
31171             callback();
31172         }
31173     },
31174
31175     // private
31176     hideAction : function(){
31177         this.setLeft("-10000px");
31178         this.setTop("-10000px");
31179         this.setStyle("visibility", "hidden");
31180     },
31181
31182     // private
31183     refreshSize : function(){
31184         this.size = this.el.getSize();
31185         this.xy = this.el.getXY();
31186         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31187     },
31188
31189     // private
31190     // z-index is managed by the DialogManager and may be overwritten at any time
31191     setZIndex : function(index){
31192         if(this.modal){
31193             this.mask.setStyle("z-index", index);
31194         }
31195         if(this.shim){
31196             this.shim.setStyle("z-index", ++index);
31197         }
31198         if(this.shadow){
31199             this.shadow.setZIndex(++index);
31200         }
31201         this.el.setStyle("z-index", ++index);
31202         if(this.proxy){
31203             this.proxy.setStyle("z-index", ++index);
31204         }
31205         if(this.resizer){
31206             this.resizer.proxy.setStyle("z-index", ++index);
31207         }
31208
31209         this.lastZIndex = index;
31210     },
31211
31212     /**
31213      * Returns the element for this dialog
31214      * @return {Roo.Element} The underlying dialog Element
31215      */
31216     getEl : function(){
31217         return this.el;
31218     }
31219 });
31220
31221 /**
31222  * @class Roo.DialogManager
31223  * Provides global access to BasicDialogs that have been created and
31224  * support for z-indexing (layering) multiple open dialogs.
31225  */
31226 Roo.DialogManager = function(){
31227     var list = {};
31228     var accessList = [];
31229     var front = null;
31230
31231     // private
31232     var sortDialogs = function(d1, d2){
31233         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31234     };
31235
31236     // private
31237     var orderDialogs = function(){
31238         accessList.sort(sortDialogs);
31239         var seed = Roo.DialogManager.zseed;
31240         for(var i = 0, len = accessList.length; i < len; i++){
31241             var dlg = accessList[i];
31242             if(dlg){
31243                 dlg.setZIndex(seed + (i*10));
31244             }
31245         }
31246     };
31247
31248     return {
31249         /**
31250          * The starting z-index for BasicDialogs (defaults to 9000)
31251          * @type Number The z-index value
31252          */
31253         zseed : 9000,
31254
31255         // private
31256         register : function(dlg){
31257             list[dlg.id] = dlg;
31258             accessList.push(dlg);
31259         },
31260
31261         // private
31262         unregister : function(dlg){
31263             delete list[dlg.id];
31264             var i=0;
31265             var len=0;
31266             if(!accessList.indexOf){
31267                 for(  i = 0, len = accessList.length; i < len; i++){
31268                     if(accessList[i] == dlg){
31269                         accessList.splice(i, 1);
31270                         return;
31271                     }
31272                 }
31273             }else{
31274                  i = accessList.indexOf(dlg);
31275                 if(i != -1){
31276                     accessList.splice(i, 1);
31277                 }
31278             }
31279         },
31280
31281         /**
31282          * Gets a registered dialog by id
31283          * @param {String/Object} id The id of the dialog or a dialog
31284          * @return {Roo.BasicDialog} this
31285          */
31286         get : function(id){
31287             return typeof id == "object" ? id : list[id];
31288         },
31289
31290         /**
31291          * Brings the specified dialog to the front
31292          * @param {String/Object} dlg The id of the dialog or a dialog
31293          * @return {Roo.BasicDialog} this
31294          */
31295         bringToFront : function(dlg){
31296             dlg = this.get(dlg);
31297             if(dlg != front){
31298                 front = dlg;
31299                 dlg._lastAccess = new Date().getTime();
31300                 orderDialogs();
31301             }
31302             return dlg;
31303         },
31304
31305         /**
31306          * Sends the specified dialog to the back
31307          * @param {String/Object} dlg The id of the dialog or a dialog
31308          * @return {Roo.BasicDialog} this
31309          */
31310         sendToBack : function(dlg){
31311             dlg = this.get(dlg);
31312             dlg._lastAccess = -(new Date().getTime());
31313             orderDialogs();
31314             return dlg;
31315         },
31316
31317         /**
31318          * Hides all dialogs
31319          */
31320         hideAll : function(){
31321             for(var id in list){
31322                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31323                     list[id].hide();
31324                 }
31325             }
31326         }
31327     };
31328 }();
31329
31330 /**
31331  * @class Roo.LayoutDialog
31332  * @extends Roo.BasicDialog
31333  * Dialog which provides adjustments for working with a layout in a Dialog.
31334  * Add your necessary layout config options to the dialog's config.<br>
31335  * Example usage (including a nested layout):
31336  * <pre><code>
31337 if(!dialog){
31338     dialog = new Roo.LayoutDialog("download-dlg", {
31339         modal: true,
31340         width:600,
31341         height:450,
31342         shadow:true,
31343         minWidth:500,
31344         minHeight:350,
31345         autoTabs:true,
31346         proxyDrag:true,
31347         // layout config merges with the dialog config
31348         center:{
31349             tabPosition: "top",
31350             alwaysShowTabs: true
31351         }
31352     });
31353     dialog.addKeyListener(27, dialog.hide, dialog);
31354     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31355     dialog.addButton("Build It!", this.getDownload, this);
31356
31357     // we can even add nested layouts
31358     var innerLayout = new Roo.BorderLayout("dl-inner", {
31359         east: {
31360             initialSize: 200,
31361             autoScroll:true,
31362             split:true
31363         },
31364         center: {
31365             autoScroll:true
31366         }
31367     });
31368     innerLayout.beginUpdate();
31369     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31370     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31371     innerLayout.endUpdate(true);
31372
31373     var layout = dialog.getLayout();
31374     layout.beginUpdate();
31375     layout.add("center", new Roo.ContentPanel("standard-panel",
31376                         {title: "Download the Source", fitToFrame:true}));
31377     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31378                {title: "Build your own roo.js"}));
31379     layout.getRegion("center").showPanel(sp);
31380     layout.endUpdate();
31381 }
31382 </code></pre>
31383     * @constructor
31384     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31385     * @param {Object} config configuration options
31386   */
31387 Roo.LayoutDialog = function(el, cfg){
31388     
31389     var config=  cfg;
31390     if (typeof(cfg) == 'undefined') {
31391         config = Roo.apply({}, el);
31392         // not sure why we use documentElement here.. - it should always be body.
31393         // IE7 borks horribly if we use documentElement.
31394         // webkit also does not like documentElement - it creates a body element...
31395         el = Roo.get( document.body || document.documentElement ).createChild();
31396         //config.autoCreate = true;
31397     }
31398     
31399     
31400     config.autoTabs = false;
31401     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31402     this.body.setStyle({overflow:"hidden", position:"relative"});
31403     this.layout = new Roo.BorderLayout(this.body.dom, config);
31404     this.layout.monitorWindowResize = false;
31405     this.el.addClass("x-dlg-auto-layout");
31406     // fix case when center region overwrites center function
31407     this.center = Roo.BasicDialog.prototype.center;
31408     this.on("show", this.layout.layout, this.layout, true);
31409     if (config.items) {
31410         var xitems = config.items;
31411         delete config.items;
31412         Roo.each(xitems, this.addxtype, this);
31413     }
31414     
31415     
31416 };
31417 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31418     /**
31419      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31420      * @deprecated
31421      */
31422     endUpdate : function(){
31423         this.layout.endUpdate();
31424     },
31425
31426     /**
31427      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31428      *  @deprecated
31429      */
31430     beginUpdate : function(){
31431         this.layout.beginUpdate();
31432     },
31433
31434     /**
31435      * Get the BorderLayout for this dialog
31436      * @return {Roo.BorderLayout}
31437      */
31438     getLayout : function(){
31439         return this.layout;
31440     },
31441
31442     showEl : function(){
31443         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31444         if(Roo.isIE7){
31445             this.layout.layout();
31446         }
31447     },
31448
31449     // private
31450     // Use the syncHeightBeforeShow config option to control this automatically
31451     syncBodyHeight : function(){
31452         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31453         if(this.layout){this.layout.layout();}
31454     },
31455     
31456       /**
31457      * Add an xtype element (actually adds to the layout.)
31458      * @return {Object} xdata xtype object data.
31459      */
31460     
31461     addxtype : function(c) {
31462         return this.layout.addxtype(c);
31463     }
31464 });/*
31465  * Based on:
31466  * Ext JS Library 1.1.1
31467  * Copyright(c) 2006-2007, Ext JS, LLC.
31468  *
31469  * Originally Released Under LGPL - original licence link has changed is not relivant.
31470  *
31471  * Fork - LGPL
31472  * <script type="text/javascript">
31473  */
31474  
31475 /**
31476  * @class Roo.MessageBox
31477  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31478  * Example usage:
31479  *<pre><code>
31480 // Basic alert:
31481 Roo.Msg.alert('Status', 'Changes saved successfully.');
31482
31483 // Prompt for user data:
31484 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31485     if (btn == 'ok'){
31486         // process text value...
31487     }
31488 });
31489
31490 // Show a dialog using config options:
31491 Roo.Msg.show({
31492    title:'Save Changes?',
31493    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31494    buttons: Roo.Msg.YESNOCANCEL,
31495    fn: processResult,
31496    animEl: 'elId'
31497 });
31498 </code></pre>
31499  * @singleton
31500  */
31501 Roo.MessageBox = function(){
31502     var dlg, opt, mask, waitTimer;
31503     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31504     var buttons, activeTextEl, bwidth;
31505
31506     // private
31507     var handleButton = function(button){
31508         dlg.hide();
31509         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31510     };
31511
31512     // private
31513     var handleHide = function(){
31514         if(opt && opt.cls){
31515             dlg.el.removeClass(opt.cls);
31516         }
31517         if(waitTimer){
31518             Roo.TaskMgr.stop(waitTimer);
31519             waitTimer = null;
31520         }
31521     };
31522
31523     // private
31524     var updateButtons = function(b){
31525         var width = 0;
31526         if(!b){
31527             buttons["ok"].hide();
31528             buttons["cancel"].hide();
31529             buttons["yes"].hide();
31530             buttons["no"].hide();
31531             dlg.footer.dom.style.display = 'none';
31532             return width;
31533         }
31534         dlg.footer.dom.style.display = '';
31535         for(var k in buttons){
31536             if(typeof buttons[k] != "function"){
31537                 if(b[k]){
31538                     buttons[k].show();
31539                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31540                     width += buttons[k].el.getWidth()+15;
31541                 }else{
31542                     buttons[k].hide();
31543                 }
31544             }
31545         }
31546         return width;
31547     };
31548
31549     // private
31550     var handleEsc = function(d, k, e){
31551         if(opt && opt.closable !== false){
31552             dlg.hide();
31553         }
31554         if(e){
31555             e.stopEvent();
31556         }
31557     };
31558
31559     return {
31560         /**
31561          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31562          * @return {Roo.BasicDialog} The BasicDialog element
31563          */
31564         getDialog : function(){
31565            if(!dlg){
31566                 dlg = new Roo.BasicDialog("x-msg-box", {
31567                     autoCreate : true,
31568                     shadow: true,
31569                     draggable: true,
31570                     resizable:false,
31571                     constraintoviewport:false,
31572                     fixedcenter:true,
31573                     collapsible : false,
31574                     shim:true,
31575                     modal: true,
31576                     width:400, height:100,
31577                     buttonAlign:"center",
31578                     closeClick : function(){
31579                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31580                             handleButton("no");
31581                         }else{
31582                             handleButton("cancel");
31583                         }
31584                     }
31585                 });
31586                 dlg.on("hide", handleHide);
31587                 mask = dlg.mask;
31588                 dlg.addKeyListener(27, handleEsc);
31589                 buttons = {};
31590                 var bt = this.buttonText;
31591                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31592                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31593                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31594                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31595                 bodyEl = dlg.body.createChild({
31596
31597                     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>'
31598                 });
31599                 msgEl = bodyEl.dom.firstChild;
31600                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31601                 textboxEl.enableDisplayMode();
31602                 textboxEl.addKeyListener([10,13], function(){
31603                     if(dlg.isVisible() && opt && opt.buttons){
31604                         if(opt.buttons.ok){
31605                             handleButton("ok");
31606                         }else if(opt.buttons.yes){
31607                             handleButton("yes");
31608                         }
31609                     }
31610                 });
31611                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31612                 textareaEl.enableDisplayMode();
31613                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31614                 progressEl.enableDisplayMode();
31615                 var pf = progressEl.dom.firstChild;
31616                 if (pf) {
31617                     pp = Roo.get(pf.firstChild);
31618                     pp.setHeight(pf.offsetHeight);
31619                 }
31620                 
31621             }
31622             return dlg;
31623         },
31624
31625         /**
31626          * Updates the message box body text
31627          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31628          * the XHTML-compliant non-breaking space character '&amp;#160;')
31629          * @return {Roo.MessageBox} This message box
31630          */
31631         updateText : function(text){
31632             if(!dlg.isVisible() && !opt.width){
31633                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31634             }
31635             msgEl.innerHTML = text || '&#160;';
31636       
31637             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31638             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31639             var w = Math.max(
31640                     Math.min(opt.width || cw , this.maxWidth), 
31641                     Math.max(opt.minWidth || this.minWidth, bwidth)
31642             );
31643             if(opt.prompt){
31644                 activeTextEl.setWidth(w);
31645             }
31646             if(dlg.isVisible()){
31647                 dlg.fixedcenter = false;
31648             }
31649             // to big, make it scroll. = But as usual stupid IE does not support
31650             // !important..
31651             
31652             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31653                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31654                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31655             } else {
31656                 bodyEl.dom.style.height = '';
31657                 bodyEl.dom.style.overflowY = '';
31658             }
31659             if (cw > w) {
31660                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31661             } else {
31662                 bodyEl.dom.style.overflowX = '';
31663             }
31664             
31665             dlg.setContentSize(w, bodyEl.getHeight());
31666             if(dlg.isVisible()){
31667                 dlg.fixedcenter = true;
31668             }
31669             return this;
31670         },
31671
31672         /**
31673          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31674          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31675          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31676          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31677          * @return {Roo.MessageBox} This message box
31678          */
31679         updateProgress : function(value, text){
31680             if(text){
31681                 this.updateText(text);
31682             }
31683             if (pp) { // weird bug on my firefox - for some reason this is not defined
31684                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31685             }
31686             return this;
31687         },        
31688
31689         /**
31690          * Returns true if the message box is currently displayed
31691          * @return {Boolean} True if the message box is visible, else false
31692          */
31693         isVisible : function(){
31694             return dlg && dlg.isVisible();  
31695         },
31696
31697         /**
31698          * Hides the message box if it is displayed
31699          */
31700         hide : function(){
31701             if(this.isVisible()){
31702                 dlg.hide();
31703             }  
31704         },
31705
31706         /**
31707          * Displays a new message box, or reinitializes an existing message box, based on the config options
31708          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31709          * The following config object properties are supported:
31710          * <pre>
31711 Property    Type             Description
31712 ----------  ---------------  ------------------------------------------------------------------------------------
31713 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31714                                    closes (defaults to undefined)
31715 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31716                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31717 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31718                                    progress and wait dialogs will ignore this property and always hide the
31719                                    close button as they can only be closed programmatically.
31720 cls               String           A custom CSS class to apply to the message box element
31721 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31722                                    displayed (defaults to 75)
31723 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31724                                    function will be btn (the name of the button that was clicked, if applicable,
31725                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31726                                    Progress and wait dialogs will ignore this option since they do not respond to
31727                                    user actions and can only be closed programmatically, so any required function
31728                                    should be called by the same code after it closes the dialog.
31729 icon              String           A CSS class that provides a background image to be used as an icon for
31730                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31731 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31732 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31733 modal             Boolean          False to allow user interaction with the page while the message box is
31734                                    displayed (defaults to true)
31735 msg               String           A string that will replace the existing message box body text (defaults
31736                                    to the XHTML-compliant non-breaking space character '&#160;')
31737 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31738 progress          Boolean          True to display a progress bar (defaults to false)
31739 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31740 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31741 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31742 title             String           The title text
31743 value             String           The string value to set into the active textbox element if displayed
31744 wait              Boolean          True to display a progress bar (defaults to false)
31745 width             Number           The width of the dialog in pixels
31746 </pre>
31747          *
31748          * Example usage:
31749          * <pre><code>
31750 Roo.Msg.show({
31751    title: 'Address',
31752    msg: 'Please enter your address:',
31753    width: 300,
31754    buttons: Roo.MessageBox.OKCANCEL,
31755    multiline: true,
31756    fn: saveAddress,
31757    animEl: 'addAddressBtn'
31758 });
31759 </code></pre>
31760          * @param {Object} config Configuration options
31761          * @return {Roo.MessageBox} This message box
31762          */
31763         show : function(options)
31764         {
31765             
31766             // this causes nightmares if you show one dialog after another
31767             // especially on callbacks..
31768              
31769             if(this.isVisible()){
31770                 
31771                 this.hide();
31772                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31773                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31774                 Roo.log("New Dialog Message:" +  options.msg )
31775                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31776                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31777                 
31778             }
31779             var d = this.getDialog();
31780             opt = options;
31781             d.setTitle(opt.title || "&#160;");
31782             d.close.setDisplayed(opt.closable !== false);
31783             activeTextEl = textboxEl;
31784             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31785             if(opt.prompt){
31786                 if(opt.multiline){
31787                     textboxEl.hide();
31788                     textareaEl.show();
31789                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31790                         opt.multiline : this.defaultTextHeight);
31791                     activeTextEl = textareaEl;
31792                 }else{
31793                     textboxEl.show();
31794                     textareaEl.hide();
31795                 }
31796             }else{
31797                 textboxEl.hide();
31798                 textareaEl.hide();
31799             }
31800             progressEl.setDisplayed(opt.progress === true);
31801             this.updateProgress(0);
31802             activeTextEl.dom.value = opt.value || "";
31803             if(opt.prompt){
31804                 dlg.setDefaultButton(activeTextEl);
31805             }else{
31806                 var bs = opt.buttons;
31807                 var db = null;
31808                 if(bs && bs.ok){
31809                     db = buttons["ok"];
31810                 }else if(bs && bs.yes){
31811                     db = buttons["yes"];
31812                 }
31813                 dlg.setDefaultButton(db);
31814             }
31815             bwidth = updateButtons(opt.buttons);
31816             this.updateText(opt.msg);
31817             if(opt.cls){
31818                 d.el.addClass(opt.cls);
31819             }
31820             d.proxyDrag = opt.proxyDrag === true;
31821             d.modal = opt.modal !== false;
31822             d.mask = opt.modal !== false ? mask : false;
31823             if(!d.isVisible()){
31824                 // force it to the end of the z-index stack so it gets a cursor in FF
31825                 document.body.appendChild(dlg.el.dom);
31826                 d.animateTarget = null;
31827                 d.show(options.animEl);
31828             }
31829             return this;
31830         },
31831
31832         /**
31833          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31834          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31835          * and closing the message box when the process is complete.
31836          * @param {String} title The title bar text
31837          * @param {String} msg The message box body text
31838          * @return {Roo.MessageBox} This message box
31839          */
31840         progress : function(title, msg){
31841             this.show({
31842                 title : title,
31843                 msg : msg,
31844                 buttons: false,
31845                 progress:true,
31846                 closable:false,
31847                 minWidth: this.minProgressWidth,
31848                 modal : true
31849             });
31850             return this;
31851         },
31852
31853         /**
31854          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31855          * If a callback function is passed it will be called after the user clicks the button, and the
31856          * id of the button that was clicked will be passed as the only parameter to the callback
31857          * (could also be the top-right close button).
31858          * @param {String} title The title bar text
31859          * @param {String} msg The message box body text
31860          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31861          * @param {Object} scope (optional) The scope of the callback function
31862          * @return {Roo.MessageBox} This message box
31863          */
31864         alert : function(title, msg, fn, scope){
31865             this.show({
31866                 title : title,
31867                 msg : msg,
31868                 buttons: this.OK,
31869                 fn: fn,
31870                 scope : scope,
31871                 modal : true
31872             });
31873             return this;
31874         },
31875
31876         /**
31877          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31878          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31879          * You are responsible for closing the message box when the process is complete.
31880          * @param {String} msg The message box body text
31881          * @param {String} title (optional) The title bar text
31882          * @return {Roo.MessageBox} This message box
31883          */
31884         wait : function(msg, title){
31885             this.show({
31886                 title : title,
31887                 msg : msg,
31888                 buttons: false,
31889                 closable:false,
31890                 progress:true,
31891                 modal:true,
31892                 width:300,
31893                 wait:true
31894             });
31895             waitTimer = Roo.TaskMgr.start({
31896                 run: function(i){
31897                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31898                 },
31899                 interval: 1000
31900             });
31901             return this;
31902         },
31903
31904         /**
31905          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31906          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31907          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31908          * @param {String} title The title bar text
31909          * @param {String} msg The message box body text
31910          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31911          * @param {Object} scope (optional) The scope of the callback function
31912          * @return {Roo.MessageBox} This message box
31913          */
31914         confirm : function(title, msg, fn, scope){
31915             this.show({
31916                 title : title,
31917                 msg : msg,
31918                 buttons: this.YESNO,
31919                 fn: fn,
31920                 scope : scope,
31921                 modal : true
31922             });
31923             return this;
31924         },
31925
31926         /**
31927          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31928          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31929          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31930          * (could also be the top-right close button) and the text that was entered will be passed as the two
31931          * parameters to the callback.
31932          * @param {String} title The title bar text
31933          * @param {String} msg The message box body text
31934          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31935          * @param {Object} scope (optional) The scope of the callback function
31936          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31937          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31938          * @return {Roo.MessageBox} This message box
31939          */
31940         prompt : function(title, msg, fn, scope, multiline){
31941             this.show({
31942                 title : title,
31943                 msg : msg,
31944                 buttons: this.OKCANCEL,
31945                 fn: fn,
31946                 minWidth:250,
31947                 scope : scope,
31948                 prompt:true,
31949                 multiline: multiline,
31950                 modal : true
31951             });
31952             return this;
31953         },
31954
31955         /**
31956          * Button config that displays a single OK button
31957          * @type Object
31958          */
31959         OK : {ok:true},
31960         /**
31961          * Button config that displays Yes and No buttons
31962          * @type Object
31963          */
31964         YESNO : {yes:true, no:true},
31965         /**
31966          * Button config that displays OK and Cancel buttons
31967          * @type Object
31968          */
31969         OKCANCEL : {ok:true, cancel:true},
31970         /**
31971          * Button config that displays Yes, No and Cancel buttons
31972          * @type Object
31973          */
31974         YESNOCANCEL : {yes:true, no:true, cancel:true},
31975
31976         /**
31977          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31978          * @type Number
31979          */
31980         defaultTextHeight : 75,
31981         /**
31982          * The maximum width in pixels of the message box (defaults to 600)
31983          * @type Number
31984          */
31985         maxWidth : 600,
31986         /**
31987          * The minimum width in pixels of the message box (defaults to 100)
31988          * @type Number
31989          */
31990         minWidth : 100,
31991         /**
31992          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31993          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31994          * @type Number
31995          */
31996         minProgressWidth : 250,
31997         /**
31998          * An object containing the default button text strings that can be overriden for localized language support.
31999          * Supported properties are: ok, cancel, yes and no.
32000          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
32001          * @type Object
32002          */
32003         buttonText : {
32004             ok : "OK",
32005             cancel : "Cancel",
32006             yes : "Yes",
32007             no : "No"
32008         }
32009     };
32010 }();
32011
32012 /**
32013  * Shorthand for {@link Roo.MessageBox}
32014  */
32015 Roo.Msg = Roo.MessageBox;/*
32016  * Based on:
32017  * Ext JS Library 1.1.1
32018  * Copyright(c) 2006-2007, Ext JS, LLC.
32019  *
32020  * Originally Released Under LGPL - original licence link has changed is not relivant.
32021  *
32022  * Fork - LGPL
32023  * <script type="text/javascript">
32024  */
32025 /**
32026  * @class Roo.QuickTips
32027  * Provides attractive and customizable tooltips for any element.
32028  * @singleton
32029  */
32030 Roo.QuickTips = function(){
32031     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
32032     var ce, bd, xy, dd;
32033     var visible = false, disabled = true, inited = false;
32034     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32035     
32036     var onOver = function(e){
32037         if(disabled){
32038             return;
32039         }
32040         var t = e.getTarget();
32041         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32042             return;
32043         }
32044         if(ce && t == ce.el){
32045             clearTimeout(hideProc);
32046             return;
32047         }
32048         if(t && tagEls[t.id]){
32049             tagEls[t.id].el = t;
32050             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32051             return;
32052         }
32053         var ttp, et = Roo.fly(t);
32054         var ns = cfg.namespace;
32055         if(tm.interceptTitles && t.title){
32056             ttp = t.title;
32057             t.qtip = ttp;
32058             t.removeAttribute("title");
32059             e.preventDefault();
32060         }else{
32061             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32062         }
32063         if(ttp){
32064             showProc = show.defer(tm.showDelay, tm, [{
32065                 el: t, 
32066                 text: ttp, 
32067                 width: et.getAttributeNS(ns, cfg.width),
32068                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32069                 title: et.getAttributeNS(ns, cfg.title),
32070                     cls: et.getAttributeNS(ns, cfg.cls)
32071             }]);
32072         }
32073     };
32074     
32075     var onOut = function(e){
32076         clearTimeout(showProc);
32077         var t = e.getTarget();
32078         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32079             hideProc = setTimeout(hide, tm.hideDelay);
32080         }
32081     };
32082     
32083     var onMove = function(e){
32084         if(disabled){
32085             return;
32086         }
32087         xy = e.getXY();
32088         xy[1] += 18;
32089         if(tm.trackMouse && ce){
32090             el.setXY(xy);
32091         }
32092     };
32093     
32094     var onDown = function(e){
32095         clearTimeout(showProc);
32096         clearTimeout(hideProc);
32097         if(!e.within(el)){
32098             if(tm.hideOnClick){
32099                 hide();
32100                 tm.disable();
32101                 tm.enable.defer(100, tm);
32102             }
32103         }
32104     };
32105     
32106     var getPad = function(){
32107         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32108     };
32109
32110     var show = function(o){
32111         if(disabled){
32112             return;
32113         }
32114         clearTimeout(dismissProc);
32115         ce = o;
32116         if(removeCls){ // in case manually hidden
32117             el.removeClass(removeCls);
32118             removeCls = null;
32119         }
32120         if(ce.cls){
32121             el.addClass(ce.cls);
32122             removeCls = ce.cls;
32123         }
32124         if(ce.title){
32125             tipTitle.update(ce.title);
32126             tipTitle.show();
32127         }else{
32128             tipTitle.update('');
32129             tipTitle.hide();
32130         }
32131         el.dom.style.width  = tm.maxWidth+'px';
32132         //tipBody.dom.style.width = '';
32133         tipBodyText.update(o.text);
32134         var p = getPad(), w = ce.width;
32135         if(!w){
32136             var td = tipBodyText.dom;
32137             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32138             if(aw > tm.maxWidth){
32139                 w = tm.maxWidth;
32140             }else if(aw < tm.minWidth){
32141                 w = tm.minWidth;
32142             }else{
32143                 w = aw;
32144             }
32145         }
32146         //tipBody.setWidth(w);
32147         el.setWidth(parseInt(w, 10) + p);
32148         if(ce.autoHide === false){
32149             close.setDisplayed(true);
32150             if(dd){
32151                 dd.unlock();
32152             }
32153         }else{
32154             close.setDisplayed(false);
32155             if(dd){
32156                 dd.lock();
32157             }
32158         }
32159         if(xy){
32160             el.avoidY = xy[1]-18;
32161             el.setXY(xy);
32162         }
32163         if(tm.animate){
32164             el.setOpacity(.1);
32165             el.setStyle("visibility", "visible");
32166             el.fadeIn({callback: afterShow});
32167         }else{
32168             afterShow();
32169         }
32170     };
32171     
32172     var afterShow = function(){
32173         if(ce){
32174             el.show();
32175             esc.enable();
32176             if(tm.autoDismiss && ce.autoHide !== false){
32177                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32178             }
32179         }
32180     };
32181     
32182     var hide = function(noanim){
32183         clearTimeout(dismissProc);
32184         clearTimeout(hideProc);
32185         ce = null;
32186         if(el.isVisible()){
32187             esc.disable();
32188             if(noanim !== true && tm.animate){
32189                 el.fadeOut({callback: afterHide});
32190             }else{
32191                 afterHide();
32192             } 
32193         }
32194     };
32195     
32196     var afterHide = function(){
32197         el.hide();
32198         if(removeCls){
32199             el.removeClass(removeCls);
32200             removeCls = null;
32201         }
32202     };
32203     
32204     return {
32205         /**
32206         * @cfg {Number} minWidth
32207         * The minimum width of the quick tip (defaults to 40)
32208         */
32209        minWidth : 40,
32210         /**
32211         * @cfg {Number} maxWidth
32212         * The maximum width of the quick tip (defaults to 300)
32213         */
32214        maxWidth : 300,
32215         /**
32216         * @cfg {Boolean} interceptTitles
32217         * True to automatically use the element's DOM title value if available (defaults to false)
32218         */
32219        interceptTitles : false,
32220         /**
32221         * @cfg {Boolean} trackMouse
32222         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32223         */
32224        trackMouse : false,
32225         /**
32226         * @cfg {Boolean} hideOnClick
32227         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32228         */
32229        hideOnClick : true,
32230         /**
32231         * @cfg {Number} showDelay
32232         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32233         */
32234        showDelay : 500,
32235         /**
32236         * @cfg {Number} hideDelay
32237         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32238         */
32239        hideDelay : 200,
32240         /**
32241         * @cfg {Boolean} autoHide
32242         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32243         * Used in conjunction with hideDelay.
32244         */
32245        autoHide : true,
32246         /**
32247         * @cfg {Boolean}
32248         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32249         * (defaults to true).  Used in conjunction with autoDismissDelay.
32250         */
32251        autoDismiss : true,
32252         /**
32253         * @cfg {Number}
32254         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32255         */
32256        autoDismissDelay : 5000,
32257        /**
32258         * @cfg {Boolean} animate
32259         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32260         */
32261        animate : false,
32262
32263        /**
32264         * @cfg {String} title
32265         * Title text to display (defaults to '').  This can be any valid HTML markup.
32266         */
32267         title: '',
32268        /**
32269         * @cfg {String} text
32270         * Body text to display (defaults to '').  This can be any valid HTML markup.
32271         */
32272         text : '',
32273        /**
32274         * @cfg {String} cls
32275         * A CSS class to apply to the base quick tip element (defaults to '').
32276         */
32277         cls : '',
32278        /**
32279         * @cfg {Number} width
32280         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32281         * minWidth or maxWidth.
32282         */
32283         width : null,
32284
32285     /**
32286      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32287      * or display QuickTips in a page.
32288      */
32289        init : function(){
32290           tm = Roo.QuickTips;
32291           cfg = tm.tagConfig;
32292           if(!inited){
32293               if(!Roo.isReady){ // allow calling of init() before onReady
32294                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32295                   return;
32296               }
32297               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32298               el.fxDefaults = {stopFx: true};
32299               // maximum custom styling
32300               //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>');
32301               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>');              
32302               tipTitle = el.child('h3');
32303               tipTitle.enableDisplayMode("block");
32304               tipBody = el.child('div.x-tip-bd');
32305               tipBodyText = el.child('div.x-tip-bd-inner');
32306               //bdLeft = el.child('div.x-tip-bd-left');
32307               //bdRight = el.child('div.x-tip-bd-right');
32308               close = el.child('div.x-tip-close');
32309               close.enableDisplayMode("block");
32310               close.on("click", hide);
32311               var d = Roo.get(document);
32312               d.on("mousedown", onDown);
32313               d.on("mouseover", onOver);
32314               d.on("mouseout", onOut);
32315               d.on("mousemove", onMove);
32316               esc = d.addKeyListener(27, hide);
32317               esc.disable();
32318               if(Roo.dd.DD){
32319                   dd = el.initDD("default", null, {
32320                       onDrag : function(){
32321                           el.sync();  
32322                       }
32323                   });
32324                   dd.setHandleElId(tipTitle.id);
32325                   dd.lock();
32326               }
32327               inited = true;
32328           }
32329           this.enable(); 
32330        },
32331
32332     /**
32333      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32334      * are supported:
32335      * <pre>
32336 Property    Type                   Description
32337 ----------  ---------------------  ------------------------------------------------------------------------
32338 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32339      * </ul>
32340      * @param {Object} config The config object
32341      */
32342        register : function(config){
32343            var cs = config instanceof Array ? config : arguments;
32344            for(var i = 0, len = cs.length; i < len; i++) {
32345                var c = cs[i];
32346                var target = c.target;
32347                if(target){
32348                    if(target instanceof Array){
32349                        for(var j = 0, jlen = target.length; j < jlen; j++){
32350                            tagEls[target[j]] = c;
32351                        }
32352                    }else{
32353                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32354                    }
32355                }
32356            }
32357        },
32358
32359     /**
32360      * Removes this quick tip from its element and destroys it.
32361      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32362      */
32363        unregister : function(el){
32364            delete tagEls[Roo.id(el)];
32365        },
32366
32367     /**
32368      * Enable this quick tip.
32369      */
32370        enable : function(){
32371            if(inited && disabled){
32372                locks.pop();
32373                if(locks.length < 1){
32374                    disabled = false;
32375                }
32376            }
32377        },
32378
32379     /**
32380      * Disable this quick tip.
32381      */
32382        disable : function(){
32383           disabled = true;
32384           clearTimeout(showProc);
32385           clearTimeout(hideProc);
32386           clearTimeout(dismissProc);
32387           if(ce){
32388               hide(true);
32389           }
32390           locks.push(1);
32391        },
32392
32393     /**
32394      * Returns true if the quick tip is enabled, else false.
32395      */
32396        isEnabled : function(){
32397             return !disabled;
32398        },
32399
32400         // private
32401        tagConfig : {
32402            namespace : "ext",
32403            attribute : "qtip",
32404            width : "width",
32405            target : "target",
32406            title : "qtitle",
32407            hide : "hide",
32408            cls : "qclass"
32409        }
32410    };
32411 }();
32412
32413 // backwards compat
32414 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32415  * Based on:
32416  * Ext JS Library 1.1.1
32417  * Copyright(c) 2006-2007, Ext JS, LLC.
32418  *
32419  * Originally Released Under LGPL - original licence link has changed is not relivant.
32420  *
32421  * Fork - LGPL
32422  * <script type="text/javascript">
32423  */
32424  
32425
32426 /**
32427  * @class Roo.tree.TreePanel
32428  * @extends Roo.data.Tree
32429
32430  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32431  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32432  * @cfg {Boolean} enableDD true to enable drag and drop
32433  * @cfg {Boolean} enableDrag true to enable just drag
32434  * @cfg {Boolean} enableDrop true to enable just drop
32435  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32436  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32437  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32438  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32439  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32440  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32441  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32442  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32443  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32444  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32445  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32446  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32447  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32448  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32449  * @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>
32450  * @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>
32451  * 
32452  * @constructor
32453  * @param {String/HTMLElement/Element} el The container element
32454  * @param {Object} config
32455  */
32456 Roo.tree.TreePanel = function(el, config){
32457     var root = false;
32458     var loader = false;
32459     if (config.root) {
32460         root = config.root;
32461         delete config.root;
32462     }
32463     if (config.loader) {
32464         loader = config.loader;
32465         delete config.loader;
32466     }
32467     
32468     Roo.apply(this, config);
32469     Roo.tree.TreePanel.superclass.constructor.call(this);
32470     this.el = Roo.get(el);
32471     this.el.addClass('x-tree');
32472     //console.log(root);
32473     if (root) {
32474         this.setRootNode( Roo.factory(root, Roo.tree));
32475     }
32476     if (loader) {
32477         this.loader = Roo.factory(loader, Roo.tree);
32478     }
32479    /**
32480     * Read-only. The id of the container element becomes this TreePanel's id.
32481     */
32482     this.id = this.el.id;
32483     this.addEvents({
32484         /**
32485         * @event beforeload
32486         * Fires before a node is loaded, return false to cancel
32487         * @param {Node} node The node being loaded
32488         */
32489         "beforeload" : true,
32490         /**
32491         * @event load
32492         * Fires when a node is loaded
32493         * @param {Node} node The node that was loaded
32494         */
32495         "load" : true,
32496         /**
32497         * @event textchange
32498         * Fires when the text for a node is changed
32499         * @param {Node} node The node
32500         * @param {String} text The new text
32501         * @param {String} oldText The old text
32502         */
32503         "textchange" : true,
32504         /**
32505         * @event beforeexpand
32506         * Fires before a node is expanded, return false to cancel.
32507         * @param {Node} node The node
32508         * @param {Boolean} deep
32509         * @param {Boolean} anim
32510         */
32511         "beforeexpand" : true,
32512         /**
32513         * @event beforecollapse
32514         * Fires before a node is collapsed, return false to cancel.
32515         * @param {Node} node The node
32516         * @param {Boolean} deep
32517         * @param {Boolean} anim
32518         */
32519         "beforecollapse" : true,
32520         /**
32521         * @event expand
32522         * Fires when a node is expanded
32523         * @param {Node} node The node
32524         */
32525         "expand" : true,
32526         /**
32527         * @event disabledchange
32528         * Fires when the disabled status of a node changes
32529         * @param {Node} node The node
32530         * @param {Boolean} disabled
32531         */
32532         "disabledchange" : true,
32533         /**
32534         * @event collapse
32535         * Fires when a node is collapsed
32536         * @param {Node} node The node
32537         */
32538         "collapse" : true,
32539         /**
32540         * @event beforeclick
32541         * Fires before click processing on a node. Return false to cancel the default action.
32542         * @param {Node} node The node
32543         * @param {Roo.EventObject} e The event object
32544         */
32545         "beforeclick":true,
32546         /**
32547         * @event checkchange
32548         * Fires when a node with a checkbox's checked property changes
32549         * @param {Node} this This node
32550         * @param {Boolean} checked
32551         */
32552         "checkchange":true,
32553         /**
32554         * @event click
32555         * Fires when a node is clicked
32556         * @param {Node} node The node
32557         * @param {Roo.EventObject} e The event object
32558         */
32559         "click":true,
32560         /**
32561         * @event dblclick
32562         * Fires when a node is double clicked
32563         * @param {Node} node The node
32564         * @param {Roo.EventObject} e The event object
32565         */
32566         "dblclick":true,
32567         /**
32568         * @event contextmenu
32569         * Fires when a node is right clicked
32570         * @param {Node} node The node
32571         * @param {Roo.EventObject} e The event object
32572         */
32573         "contextmenu":true,
32574         /**
32575         * @event beforechildrenrendered
32576         * Fires right before the child nodes for a node are rendered
32577         * @param {Node} node The node
32578         */
32579         "beforechildrenrendered":true,
32580         /**
32581         * @event startdrag
32582         * Fires when a node starts being dragged
32583         * @param {Roo.tree.TreePanel} this
32584         * @param {Roo.tree.TreeNode} node
32585         * @param {event} e The raw browser event
32586         */ 
32587        "startdrag" : true,
32588        /**
32589         * @event enddrag
32590         * Fires when a drag operation is complete
32591         * @param {Roo.tree.TreePanel} this
32592         * @param {Roo.tree.TreeNode} node
32593         * @param {event} e The raw browser event
32594         */
32595        "enddrag" : true,
32596        /**
32597         * @event dragdrop
32598         * Fires when a dragged node is dropped on a valid DD target
32599         * @param {Roo.tree.TreePanel} this
32600         * @param {Roo.tree.TreeNode} node
32601         * @param {DD} dd The dd it was dropped on
32602         * @param {event} e The raw browser event
32603         */
32604        "dragdrop" : true,
32605        /**
32606         * @event beforenodedrop
32607         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32608         * passed to handlers has the following properties:<br />
32609         * <ul style="padding:5px;padding-left:16px;">
32610         * <li>tree - The TreePanel</li>
32611         * <li>target - The node being targeted for the drop</li>
32612         * <li>data - The drag data from the drag source</li>
32613         * <li>point - The point of the drop - append, above or below</li>
32614         * <li>source - The drag source</li>
32615         * <li>rawEvent - Raw mouse event</li>
32616         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32617         * to be inserted by setting them on this object.</li>
32618         * <li>cancel - Set this to true to cancel the drop.</li>
32619         * </ul>
32620         * @param {Object} dropEvent
32621         */
32622        "beforenodedrop" : true,
32623        /**
32624         * @event nodedrop
32625         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32626         * passed to handlers has the following properties:<br />
32627         * <ul style="padding:5px;padding-left:16px;">
32628         * <li>tree - The TreePanel</li>
32629         * <li>target - The node being targeted for the drop</li>
32630         * <li>data - The drag data from the drag source</li>
32631         * <li>point - The point of the drop - append, above or below</li>
32632         * <li>source - The drag source</li>
32633         * <li>rawEvent - Raw mouse event</li>
32634         * <li>dropNode - Dropped node(s).</li>
32635         * </ul>
32636         * @param {Object} dropEvent
32637         */
32638        "nodedrop" : true,
32639         /**
32640         * @event nodedragover
32641         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32642         * passed to handlers has the following properties:<br />
32643         * <ul style="padding:5px;padding-left:16px;">
32644         * <li>tree - The TreePanel</li>
32645         * <li>target - The node being targeted for the drop</li>
32646         * <li>data - The drag data from the drag source</li>
32647         * <li>point - The point of the drop - append, above or below</li>
32648         * <li>source - The drag source</li>
32649         * <li>rawEvent - Raw mouse event</li>
32650         * <li>dropNode - Drop node(s) provided by the source.</li>
32651         * <li>cancel - Set this to true to signal drop not allowed.</li>
32652         * </ul>
32653         * @param {Object} dragOverEvent
32654         */
32655        "nodedragover" : true
32656         
32657     });
32658     if(this.singleExpand){
32659        this.on("beforeexpand", this.restrictExpand, this);
32660     }
32661     if (this.editor) {
32662         this.editor.tree = this;
32663         this.editor = Roo.factory(this.editor, Roo.tree);
32664     }
32665     
32666     if (this.selModel) {
32667         this.selModel = Roo.factory(this.selModel, Roo.tree);
32668     }
32669    
32670 };
32671 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32672     rootVisible : true,
32673     animate: Roo.enableFx,
32674     lines : true,
32675     enableDD : false,
32676     hlDrop : Roo.enableFx,
32677   
32678     renderer: false,
32679     
32680     rendererTip: false,
32681     // private
32682     restrictExpand : function(node){
32683         var p = node.parentNode;
32684         if(p){
32685             if(p.expandedChild && p.expandedChild.parentNode == p){
32686                 p.expandedChild.collapse();
32687             }
32688             p.expandedChild = node;
32689         }
32690     },
32691
32692     // private override
32693     setRootNode : function(node){
32694         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32695         if(!this.rootVisible){
32696             node.ui = new Roo.tree.RootTreeNodeUI(node);
32697         }
32698         return node;
32699     },
32700
32701     /**
32702      * Returns the container element for this TreePanel
32703      */
32704     getEl : function(){
32705         return this.el;
32706     },
32707
32708     /**
32709      * Returns the default TreeLoader for this TreePanel
32710      */
32711     getLoader : function(){
32712         return this.loader;
32713     },
32714
32715     /**
32716      * Expand all nodes
32717      */
32718     expandAll : function(){
32719         this.root.expand(true);
32720     },
32721
32722     /**
32723      * Collapse all nodes
32724      */
32725     collapseAll : function(){
32726         this.root.collapse(true);
32727     },
32728
32729     /**
32730      * Returns the selection model used by this TreePanel
32731      */
32732     getSelectionModel : function(){
32733         if(!this.selModel){
32734             this.selModel = new Roo.tree.DefaultSelectionModel();
32735         }
32736         return this.selModel;
32737     },
32738
32739     /**
32740      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32741      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32742      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32743      * @return {Array}
32744      */
32745     getChecked : function(a, startNode){
32746         startNode = startNode || this.root;
32747         var r = [];
32748         var f = function(){
32749             if(this.attributes.checked){
32750                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32751             }
32752         }
32753         startNode.cascade(f);
32754         return r;
32755     },
32756
32757     /**
32758      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32759      * @param {String} path
32760      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32761      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32762      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32763      */
32764     expandPath : function(path, attr, callback){
32765         attr = attr || "id";
32766         var keys = path.split(this.pathSeparator);
32767         var curNode = this.root;
32768         if(curNode.attributes[attr] != keys[1]){ // invalid root
32769             if(callback){
32770                 callback(false, null);
32771             }
32772             return;
32773         }
32774         var index = 1;
32775         var f = function(){
32776             if(++index == keys.length){
32777                 if(callback){
32778                     callback(true, curNode);
32779                 }
32780                 return;
32781             }
32782             var c = curNode.findChild(attr, keys[index]);
32783             if(!c){
32784                 if(callback){
32785                     callback(false, curNode);
32786                 }
32787                 return;
32788             }
32789             curNode = c;
32790             c.expand(false, false, f);
32791         };
32792         curNode.expand(false, false, f);
32793     },
32794
32795     /**
32796      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32797      * @param {String} path
32798      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32799      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32800      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32801      */
32802     selectPath : function(path, attr, callback){
32803         attr = attr || "id";
32804         var keys = path.split(this.pathSeparator);
32805         var v = keys.pop();
32806         if(keys.length > 0){
32807             var f = function(success, node){
32808                 if(success && node){
32809                     var n = node.findChild(attr, v);
32810                     if(n){
32811                         n.select();
32812                         if(callback){
32813                             callback(true, n);
32814                         }
32815                     }else if(callback){
32816                         callback(false, n);
32817                     }
32818                 }else{
32819                     if(callback){
32820                         callback(false, n);
32821                     }
32822                 }
32823             };
32824             this.expandPath(keys.join(this.pathSeparator), attr, f);
32825         }else{
32826             this.root.select();
32827             if(callback){
32828                 callback(true, this.root);
32829             }
32830         }
32831     },
32832
32833     getTreeEl : function(){
32834         return this.el;
32835     },
32836
32837     /**
32838      * Trigger rendering of this TreePanel
32839      */
32840     render : function(){
32841         if (this.innerCt) {
32842             return this; // stop it rendering more than once!!
32843         }
32844         
32845         this.innerCt = this.el.createChild({tag:"ul",
32846                cls:"x-tree-root-ct " +
32847                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32848
32849         if(this.containerScroll){
32850             Roo.dd.ScrollManager.register(this.el);
32851         }
32852         if((this.enableDD || this.enableDrop) && !this.dropZone){
32853            /**
32854             * The dropZone used by this tree if drop is enabled
32855             * @type Roo.tree.TreeDropZone
32856             */
32857              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32858                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32859            });
32860         }
32861         if((this.enableDD || this.enableDrag) && !this.dragZone){
32862            /**
32863             * The dragZone used by this tree if drag is enabled
32864             * @type Roo.tree.TreeDragZone
32865             */
32866             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32867                ddGroup: this.ddGroup || "TreeDD",
32868                scroll: this.ddScroll
32869            });
32870         }
32871         this.getSelectionModel().init(this);
32872         if (!this.root) {
32873             Roo.log("ROOT not set in tree");
32874             return this;
32875         }
32876         this.root.render();
32877         if(!this.rootVisible){
32878             this.root.renderChildren();
32879         }
32880         return this;
32881     }
32882 });/*
32883  * Based on:
32884  * Ext JS Library 1.1.1
32885  * Copyright(c) 2006-2007, Ext JS, LLC.
32886  *
32887  * Originally Released Under LGPL - original licence link has changed is not relivant.
32888  *
32889  * Fork - LGPL
32890  * <script type="text/javascript">
32891  */
32892  
32893
32894 /**
32895  * @class Roo.tree.DefaultSelectionModel
32896  * @extends Roo.util.Observable
32897  * The default single selection for a TreePanel.
32898  * @param {Object} cfg Configuration
32899  */
32900 Roo.tree.DefaultSelectionModel = function(cfg){
32901    this.selNode = null;
32902    
32903    
32904    
32905    this.addEvents({
32906        /**
32907         * @event selectionchange
32908         * Fires when the selected node changes
32909         * @param {DefaultSelectionModel} this
32910         * @param {TreeNode} node the new selection
32911         */
32912        "selectionchange" : true,
32913
32914        /**
32915         * @event beforeselect
32916         * Fires before the selected node changes, return false to cancel the change
32917         * @param {DefaultSelectionModel} this
32918         * @param {TreeNode} node the new selection
32919         * @param {TreeNode} node the old selection
32920         */
32921        "beforeselect" : true
32922    });
32923    
32924     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32925 };
32926
32927 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32928     init : function(tree){
32929         this.tree = tree;
32930         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32931         tree.on("click", this.onNodeClick, this);
32932     },
32933     
32934     onNodeClick : function(node, e){
32935         if (e.ctrlKey && this.selNode == node)  {
32936             this.unselect(node);
32937             return;
32938         }
32939         this.select(node);
32940     },
32941     
32942     /**
32943      * Select a node.
32944      * @param {TreeNode} node The node to select
32945      * @return {TreeNode} The selected node
32946      */
32947     select : function(node){
32948         var last = this.selNode;
32949         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32950             if(last){
32951                 last.ui.onSelectedChange(false);
32952             }
32953             this.selNode = node;
32954             node.ui.onSelectedChange(true);
32955             this.fireEvent("selectionchange", this, node, last);
32956         }
32957         return node;
32958     },
32959     
32960     /**
32961      * Deselect a node.
32962      * @param {TreeNode} node The node to unselect
32963      */
32964     unselect : function(node){
32965         if(this.selNode == node){
32966             this.clearSelections();
32967         }    
32968     },
32969     
32970     /**
32971      * Clear all selections
32972      */
32973     clearSelections : function(){
32974         var n = this.selNode;
32975         if(n){
32976             n.ui.onSelectedChange(false);
32977             this.selNode = null;
32978             this.fireEvent("selectionchange", this, null);
32979         }
32980         return n;
32981     },
32982     
32983     /**
32984      * Get the selected node
32985      * @return {TreeNode} The selected node
32986      */
32987     getSelectedNode : function(){
32988         return this.selNode;    
32989     },
32990     
32991     /**
32992      * Returns true if the node is selected
32993      * @param {TreeNode} node The node to check
32994      * @return {Boolean}
32995      */
32996     isSelected : function(node){
32997         return this.selNode == node;  
32998     },
32999
33000     /**
33001      * Selects the node above the selected node in the tree, intelligently walking the nodes
33002      * @return TreeNode The new selection
33003      */
33004     selectPrevious : function(){
33005         var s = this.selNode || this.lastSelNode;
33006         if(!s){
33007             return null;
33008         }
33009         var ps = s.previousSibling;
33010         if(ps){
33011             if(!ps.isExpanded() || ps.childNodes.length < 1){
33012                 return this.select(ps);
33013             } else{
33014                 var lc = ps.lastChild;
33015                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
33016                     lc = lc.lastChild;
33017                 }
33018                 return this.select(lc);
33019             }
33020         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
33021             return this.select(s.parentNode);
33022         }
33023         return null;
33024     },
33025
33026     /**
33027      * Selects the node above the selected node in the tree, intelligently walking the nodes
33028      * @return TreeNode The new selection
33029      */
33030     selectNext : function(){
33031         var s = this.selNode || this.lastSelNode;
33032         if(!s){
33033             return null;
33034         }
33035         if(s.firstChild && s.isExpanded()){
33036              return this.select(s.firstChild);
33037          }else if(s.nextSibling){
33038              return this.select(s.nextSibling);
33039          }else if(s.parentNode){
33040             var newS = null;
33041             s.parentNode.bubble(function(){
33042                 if(this.nextSibling){
33043                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33044                     return false;
33045                 }
33046             });
33047             return newS;
33048          }
33049         return null;
33050     },
33051
33052     onKeyDown : function(e){
33053         var s = this.selNode || this.lastSelNode;
33054         // undesirable, but required
33055         var sm = this;
33056         if(!s){
33057             return;
33058         }
33059         var k = e.getKey();
33060         switch(k){
33061              case e.DOWN:
33062                  e.stopEvent();
33063                  this.selectNext();
33064              break;
33065              case e.UP:
33066                  e.stopEvent();
33067                  this.selectPrevious();
33068              break;
33069              case e.RIGHT:
33070                  e.preventDefault();
33071                  if(s.hasChildNodes()){
33072                      if(!s.isExpanded()){
33073                          s.expand();
33074                      }else if(s.firstChild){
33075                          this.select(s.firstChild, e);
33076                      }
33077                  }
33078              break;
33079              case e.LEFT:
33080                  e.preventDefault();
33081                  if(s.hasChildNodes() && s.isExpanded()){
33082                      s.collapse();
33083                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33084                      this.select(s.parentNode, e);
33085                  }
33086              break;
33087         };
33088     }
33089 });
33090
33091 /**
33092  * @class Roo.tree.MultiSelectionModel
33093  * @extends Roo.util.Observable
33094  * Multi selection for a TreePanel.
33095  * @param {Object} cfg Configuration
33096  */
33097 Roo.tree.MultiSelectionModel = function(){
33098    this.selNodes = [];
33099    this.selMap = {};
33100    this.addEvents({
33101        /**
33102         * @event selectionchange
33103         * Fires when the selected nodes change
33104         * @param {MultiSelectionModel} this
33105         * @param {Array} nodes Array of the selected nodes
33106         */
33107        "selectionchange" : true
33108    });
33109    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33110    
33111 };
33112
33113 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33114     init : function(tree){
33115         this.tree = tree;
33116         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33117         tree.on("click", this.onNodeClick, this);
33118     },
33119     
33120     onNodeClick : function(node, e){
33121         this.select(node, e, e.ctrlKey);
33122     },
33123     
33124     /**
33125      * Select a node.
33126      * @param {TreeNode} node The node to select
33127      * @param {EventObject} e (optional) An event associated with the selection
33128      * @param {Boolean} keepExisting True to retain existing selections
33129      * @return {TreeNode} The selected node
33130      */
33131     select : function(node, e, keepExisting){
33132         if(keepExisting !== true){
33133             this.clearSelections(true);
33134         }
33135         if(this.isSelected(node)){
33136             this.lastSelNode = node;
33137             return node;
33138         }
33139         this.selNodes.push(node);
33140         this.selMap[node.id] = node;
33141         this.lastSelNode = node;
33142         node.ui.onSelectedChange(true);
33143         this.fireEvent("selectionchange", this, this.selNodes);
33144         return node;
33145     },
33146     
33147     /**
33148      * Deselect a node.
33149      * @param {TreeNode} node The node to unselect
33150      */
33151     unselect : function(node){
33152         if(this.selMap[node.id]){
33153             node.ui.onSelectedChange(false);
33154             var sn = this.selNodes;
33155             var index = -1;
33156             if(sn.indexOf){
33157                 index = sn.indexOf(node);
33158             }else{
33159                 for(var i = 0, len = sn.length; i < len; i++){
33160                     if(sn[i] == node){
33161                         index = i;
33162                         break;
33163                     }
33164                 }
33165             }
33166             if(index != -1){
33167                 this.selNodes.splice(index, 1);
33168             }
33169             delete this.selMap[node.id];
33170             this.fireEvent("selectionchange", this, this.selNodes);
33171         }
33172     },
33173     
33174     /**
33175      * Clear all selections
33176      */
33177     clearSelections : function(suppressEvent){
33178         var sn = this.selNodes;
33179         if(sn.length > 0){
33180             for(var i = 0, len = sn.length; i < len; i++){
33181                 sn[i].ui.onSelectedChange(false);
33182             }
33183             this.selNodes = [];
33184             this.selMap = {};
33185             if(suppressEvent !== true){
33186                 this.fireEvent("selectionchange", this, this.selNodes);
33187             }
33188         }
33189     },
33190     
33191     /**
33192      * Returns true if the node is selected
33193      * @param {TreeNode} node The node to check
33194      * @return {Boolean}
33195      */
33196     isSelected : function(node){
33197         return this.selMap[node.id] ? true : false;  
33198     },
33199     
33200     /**
33201      * Returns an array of the selected nodes
33202      * @return {Array}
33203      */
33204     getSelectedNodes : function(){
33205         return this.selNodes;    
33206     },
33207
33208     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33209
33210     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33211
33212     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33213 });/*
33214  * Based on:
33215  * Ext JS Library 1.1.1
33216  * Copyright(c) 2006-2007, Ext JS, LLC.
33217  *
33218  * Originally Released Under LGPL - original licence link has changed is not relivant.
33219  *
33220  * Fork - LGPL
33221  * <script type="text/javascript">
33222  */
33223  
33224 /**
33225  * @class Roo.tree.TreeNode
33226  * @extends Roo.data.Node
33227  * @cfg {String} text The text for this node
33228  * @cfg {Boolean} expanded true to start the node expanded
33229  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33230  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33231  * @cfg {Boolean} disabled true to start the node disabled
33232  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33233  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33234  * @cfg {String} cls A css class to be added to the node
33235  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33236  * @cfg {String} href URL of the link used for the node (defaults to #)
33237  * @cfg {String} hrefTarget target frame for the link
33238  * @cfg {String} qtip An Ext QuickTip for the node
33239  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33240  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33241  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33242  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33243  * (defaults to undefined with no checkbox rendered)
33244  * @constructor
33245  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33246  */
33247 Roo.tree.TreeNode = function(attributes){
33248     attributes = attributes || {};
33249     if(typeof attributes == "string"){
33250         attributes = {text: attributes};
33251     }
33252     this.childrenRendered = false;
33253     this.rendered = false;
33254     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33255     this.expanded = attributes.expanded === true;
33256     this.isTarget = attributes.isTarget !== false;
33257     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33258     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33259
33260     /**
33261      * Read-only. The text for this node. To change it use setText().
33262      * @type String
33263      */
33264     this.text = attributes.text;
33265     /**
33266      * True if this node is disabled.
33267      * @type Boolean
33268      */
33269     this.disabled = attributes.disabled === true;
33270
33271     this.addEvents({
33272         /**
33273         * @event textchange
33274         * Fires when the text for this node is changed
33275         * @param {Node} this This node
33276         * @param {String} text The new text
33277         * @param {String} oldText The old text
33278         */
33279         "textchange" : true,
33280         /**
33281         * @event beforeexpand
33282         * Fires before this node is expanded, return false to cancel.
33283         * @param {Node} this This node
33284         * @param {Boolean} deep
33285         * @param {Boolean} anim
33286         */
33287         "beforeexpand" : true,
33288         /**
33289         * @event beforecollapse
33290         * Fires before this node is collapsed, return false to cancel.
33291         * @param {Node} this This node
33292         * @param {Boolean} deep
33293         * @param {Boolean} anim
33294         */
33295         "beforecollapse" : true,
33296         /**
33297         * @event expand
33298         * Fires when this node is expanded
33299         * @param {Node} this This node
33300         */
33301         "expand" : true,
33302         /**
33303         * @event disabledchange
33304         * Fires when the disabled status of this node changes
33305         * @param {Node} this This node
33306         * @param {Boolean} disabled
33307         */
33308         "disabledchange" : true,
33309         /**
33310         * @event collapse
33311         * Fires when this node is collapsed
33312         * @param {Node} this This node
33313         */
33314         "collapse" : true,
33315         /**
33316         * @event beforeclick
33317         * Fires before click processing. Return false to cancel the default action.
33318         * @param {Node} this This node
33319         * @param {Roo.EventObject} e The event object
33320         */
33321         "beforeclick":true,
33322         /**
33323         * @event checkchange
33324         * Fires when a node with a checkbox's checked property changes
33325         * @param {Node} this This node
33326         * @param {Boolean} checked
33327         */
33328         "checkchange":true,
33329         /**
33330         * @event click
33331         * Fires when this node is clicked
33332         * @param {Node} this This node
33333         * @param {Roo.EventObject} e The event object
33334         */
33335         "click":true,
33336         /**
33337         * @event dblclick
33338         * Fires when this node is double clicked
33339         * @param {Node} this This node
33340         * @param {Roo.EventObject} e The event object
33341         */
33342         "dblclick":true,
33343         /**
33344         * @event contextmenu
33345         * Fires when this node is right clicked
33346         * @param {Node} this This node
33347         * @param {Roo.EventObject} e The event object
33348         */
33349         "contextmenu":true,
33350         /**
33351         * @event beforechildrenrendered
33352         * Fires right before the child nodes for this node are rendered
33353         * @param {Node} this This node
33354         */
33355         "beforechildrenrendered":true
33356     });
33357
33358     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33359
33360     /**
33361      * Read-only. The UI for this node
33362      * @type TreeNodeUI
33363      */
33364     this.ui = new uiClass(this);
33365     
33366     // finally support items[]
33367     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33368         return;
33369     }
33370     
33371     
33372     Roo.each(this.attributes.items, function(c) {
33373         this.appendChild(Roo.factory(c,Roo.Tree));
33374     }, this);
33375     delete this.attributes.items;
33376     
33377     
33378     
33379 };
33380 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33381     preventHScroll: true,
33382     /**
33383      * Returns true if this node is expanded
33384      * @return {Boolean}
33385      */
33386     isExpanded : function(){
33387         return this.expanded;
33388     },
33389
33390     /**
33391      * Returns the UI object for this node
33392      * @return {TreeNodeUI}
33393      */
33394     getUI : function(){
33395         return this.ui;
33396     },
33397
33398     // private override
33399     setFirstChild : function(node){
33400         var of = this.firstChild;
33401         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33402         if(this.childrenRendered && of && node != of){
33403             of.renderIndent(true, true);
33404         }
33405         if(this.rendered){
33406             this.renderIndent(true, true);
33407         }
33408     },
33409
33410     // private override
33411     setLastChild : function(node){
33412         var ol = this.lastChild;
33413         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33414         if(this.childrenRendered && ol && node != ol){
33415             ol.renderIndent(true, true);
33416         }
33417         if(this.rendered){
33418             this.renderIndent(true, true);
33419         }
33420     },
33421
33422     // these methods are overridden to provide lazy rendering support
33423     // private override
33424     appendChild : function()
33425     {
33426         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33427         if(node && this.childrenRendered){
33428             node.render();
33429         }
33430         this.ui.updateExpandIcon();
33431         return node;
33432     },
33433
33434     // private override
33435     removeChild : function(node){
33436         this.ownerTree.getSelectionModel().unselect(node);
33437         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33438         // if it's been rendered remove dom node
33439         if(this.childrenRendered){
33440             node.ui.remove();
33441         }
33442         if(this.childNodes.length < 1){
33443             this.collapse(false, false);
33444         }else{
33445             this.ui.updateExpandIcon();
33446         }
33447         if(!this.firstChild) {
33448             this.childrenRendered = false;
33449         }
33450         return node;
33451     },
33452
33453     // private override
33454     insertBefore : function(node, refNode){
33455         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33456         if(newNode && refNode && this.childrenRendered){
33457             node.render();
33458         }
33459         this.ui.updateExpandIcon();
33460         return newNode;
33461     },
33462
33463     /**
33464      * Sets the text for this node
33465      * @param {String} text
33466      */
33467     setText : function(text){
33468         var oldText = this.text;
33469         this.text = text;
33470         this.attributes.text = text;
33471         if(this.rendered){ // event without subscribing
33472             this.ui.onTextChange(this, text, oldText);
33473         }
33474         this.fireEvent("textchange", this, text, oldText);
33475     },
33476
33477     /**
33478      * Triggers selection of this node
33479      */
33480     select : function(){
33481         this.getOwnerTree().getSelectionModel().select(this);
33482     },
33483
33484     /**
33485      * Triggers deselection of this node
33486      */
33487     unselect : function(){
33488         this.getOwnerTree().getSelectionModel().unselect(this);
33489     },
33490
33491     /**
33492      * Returns true if this node is selected
33493      * @return {Boolean}
33494      */
33495     isSelected : function(){
33496         return this.getOwnerTree().getSelectionModel().isSelected(this);
33497     },
33498
33499     /**
33500      * Expand this node.
33501      * @param {Boolean} deep (optional) True to expand all children as well
33502      * @param {Boolean} anim (optional) false to cancel the default animation
33503      * @param {Function} callback (optional) A callback to be called when
33504      * expanding this node completes (does not wait for deep expand to complete).
33505      * Called with 1 parameter, this node.
33506      */
33507     expand : function(deep, anim, callback){
33508         if(!this.expanded){
33509             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33510                 return;
33511             }
33512             if(!this.childrenRendered){
33513                 this.renderChildren();
33514             }
33515             this.expanded = true;
33516             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33517                 this.ui.animExpand(function(){
33518                     this.fireEvent("expand", this);
33519                     if(typeof callback == "function"){
33520                         callback(this);
33521                     }
33522                     if(deep === true){
33523                         this.expandChildNodes(true);
33524                     }
33525                 }.createDelegate(this));
33526                 return;
33527             }else{
33528                 this.ui.expand();
33529                 this.fireEvent("expand", this);
33530                 if(typeof callback == "function"){
33531                     callback(this);
33532                 }
33533             }
33534         }else{
33535            if(typeof callback == "function"){
33536                callback(this);
33537            }
33538         }
33539         if(deep === true){
33540             this.expandChildNodes(true);
33541         }
33542     },
33543
33544     isHiddenRoot : function(){
33545         return this.isRoot && !this.getOwnerTree().rootVisible;
33546     },
33547
33548     /**
33549      * Collapse this node.
33550      * @param {Boolean} deep (optional) True to collapse all children as well
33551      * @param {Boolean} anim (optional) false to cancel the default animation
33552      */
33553     collapse : function(deep, anim){
33554         if(this.expanded && !this.isHiddenRoot()){
33555             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33556                 return;
33557             }
33558             this.expanded = false;
33559             if((this.getOwnerTree().animate && anim !== false) || anim){
33560                 this.ui.animCollapse(function(){
33561                     this.fireEvent("collapse", this);
33562                     if(deep === true){
33563                         this.collapseChildNodes(true);
33564                     }
33565                 }.createDelegate(this));
33566                 return;
33567             }else{
33568                 this.ui.collapse();
33569                 this.fireEvent("collapse", this);
33570             }
33571         }
33572         if(deep === true){
33573             var cs = this.childNodes;
33574             for(var i = 0, len = cs.length; i < len; i++) {
33575                 cs[i].collapse(true, false);
33576             }
33577         }
33578     },
33579
33580     // private
33581     delayedExpand : function(delay){
33582         if(!this.expandProcId){
33583             this.expandProcId = this.expand.defer(delay, this);
33584         }
33585     },
33586
33587     // private
33588     cancelExpand : function(){
33589         if(this.expandProcId){
33590             clearTimeout(this.expandProcId);
33591         }
33592         this.expandProcId = false;
33593     },
33594
33595     /**
33596      * Toggles expanded/collapsed state of the node
33597      */
33598     toggle : function(){
33599         if(this.expanded){
33600             this.collapse();
33601         }else{
33602             this.expand();
33603         }
33604     },
33605
33606     /**
33607      * Ensures all parent nodes are expanded
33608      */
33609     ensureVisible : function(callback){
33610         var tree = this.getOwnerTree();
33611         tree.expandPath(this.parentNode.getPath(), false, function(){
33612             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33613             Roo.callback(callback);
33614         }.createDelegate(this));
33615     },
33616
33617     /**
33618      * Expand all child nodes
33619      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33620      */
33621     expandChildNodes : function(deep){
33622         var cs = this.childNodes;
33623         for(var i = 0, len = cs.length; i < len; i++) {
33624                 cs[i].expand(deep);
33625         }
33626     },
33627
33628     /**
33629      * Collapse all child nodes
33630      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33631      */
33632     collapseChildNodes : function(deep){
33633         var cs = this.childNodes;
33634         for(var i = 0, len = cs.length; i < len; i++) {
33635                 cs[i].collapse(deep);
33636         }
33637     },
33638
33639     /**
33640      * Disables this node
33641      */
33642     disable : function(){
33643         this.disabled = true;
33644         this.unselect();
33645         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33646             this.ui.onDisableChange(this, true);
33647         }
33648         this.fireEvent("disabledchange", this, true);
33649     },
33650
33651     /**
33652      * Enables this node
33653      */
33654     enable : function(){
33655         this.disabled = false;
33656         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33657             this.ui.onDisableChange(this, false);
33658         }
33659         this.fireEvent("disabledchange", this, false);
33660     },
33661
33662     // private
33663     renderChildren : function(suppressEvent){
33664         if(suppressEvent !== false){
33665             this.fireEvent("beforechildrenrendered", this);
33666         }
33667         var cs = this.childNodes;
33668         for(var i = 0, len = cs.length; i < len; i++){
33669             cs[i].render(true);
33670         }
33671         this.childrenRendered = true;
33672     },
33673
33674     // private
33675     sort : function(fn, scope){
33676         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33677         if(this.childrenRendered){
33678             var cs = this.childNodes;
33679             for(var i = 0, len = cs.length; i < len; i++){
33680                 cs[i].render(true);
33681             }
33682         }
33683     },
33684
33685     // private
33686     render : function(bulkRender){
33687         this.ui.render(bulkRender);
33688         if(!this.rendered){
33689             this.rendered = true;
33690             if(this.expanded){
33691                 this.expanded = false;
33692                 this.expand(false, false);
33693             }
33694         }
33695     },
33696
33697     // private
33698     renderIndent : function(deep, refresh){
33699         if(refresh){
33700             this.ui.childIndent = null;
33701         }
33702         this.ui.renderIndent();
33703         if(deep === true && this.childrenRendered){
33704             var cs = this.childNodes;
33705             for(var i = 0, len = cs.length; i < len; i++){
33706                 cs[i].renderIndent(true, refresh);
33707             }
33708         }
33709     }
33710 });/*
33711  * Based on:
33712  * Ext JS Library 1.1.1
33713  * Copyright(c) 2006-2007, Ext JS, LLC.
33714  *
33715  * Originally Released Under LGPL - original licence link has changed is not relivant.
33716  *
33717  * Fork - LGPL
33718  * <script type="text/javascript">
33719  */
33720  
33721 /**
33722  * @class Roo.tree.AsyncTreeNode
33723  * @extends Roo.tree.TreeNode
33724  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33725  * @constructor
33726  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33727  */
33728  Roo.tree.AsyncTreeNode = function(config){
33729     this.loaded = false;
33730     this.loading = false;
33731     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33732     /**
33733     * @event beforeload
33734     * Fires before this node is loaded, return false to cancel
33735     * @param {Node} this This node
33736     */
33737     this.addEvents({'beforeload':true, 'load': true});
33738     /**
33739     * @event load
33740     * Fires when this node is loaded
33741     * @param {Node} this This node
33742     */
33743     /**
33744      * The loader used by this node (defaults to using the tree's defined loader)
33745      * @type TreeLoader
33746      * @property loader
33747      */
33748 };
33749 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33750     expand : function(deep, anim, callback){
33751         if(this.loading){ // if an async load is already running, waiting til it's done
33752             var timer;
33753             var f = function(){
33754                 if(!this.loading){ // done loading
33755                     clearInterval(timer);
33756                     this.expand(deep, anim, callback);
33757                 }
33758             }.createDelegate(this);
33759             timer = setInterval(f, 200);
33760             return;
33761         }
33762         if(!this.loaded){
33763             if(this.fireEvent("beforeload", this) === false){
33764                 return;
33765             }
33766             this.loading = true;
33767             this.ui.beforeLoad(this);
33768             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33769             if(loader){
33770                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33771                 return;
33772             }
33773         }
33774         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33775     },
33776     
33777     /**
33778      * Returns true if this node is currently loading
33779      * @return {Boolean}
33780      */
33781     isLoading : function(){
33782         return this.loading;  
33783     },
33784     
33785     loadComplete : function(deep, anim, callback){
33786         this.loading = false;
33787         this.loaded = true;
33788         this.ui.afterLoad(this);
33789         this.fireEvent("load", this);
33790         this.expand(deep, anim, callback);
33791     },
33792     
33793     /**
33794      * Returns true if this node has been loaded
33795      * @return {Boolean}
33796      */
33797     isLoaded : function(){
33798         return this.loaded;
33799     },
33800     
33801     hasChildNodes : function(){
33802         if(!this.isLeaf() && !this.loaded){
33803             return true;
33804         }else{
33805             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33806         }
33807     },
33808
33809     /**
33810      * Trigger a reload for this node
33811      * @param {Function} callback
33812      */
33813     reload : function(callback){
33814         this.collapse(false, false);
33815         while(this.firstChild){
33816             this.removeChild(this.firstChild);
33817         }
33818         this.childrenRendered = false;
33819         this.loaded = false;
33820         if(this.isHiddenRoot()){
33821             this.expanded = false;
33822         }
33823         this.expand(false, false, callback);
33824     }
33825 });/*
33826  * Based on:
33827  * Ext JS Library 1.1.1
33828  * Copyright(c) 2006-2007, Ext JS, LLC.
33829  *
33830  * Originally Released Under LGPL - original licence link has changed is not relivant.
33831  *
33832  * Fork - LGPL
33833  * <script type="text/javascript">
33834  */
33835  
33836 /**
33837  * @class Roo.tree.TreeNodeUI
33838  * @constructor
33839  * @param {Object} node The node to render
33840  * The TreeNode UI implementation is separate from the
33841  * tree implementation. Unless you are customizing the tree UI,
33842  * you should never have to use this directly.
33843  */
33844 Roo.tree.TreeNodeUI = function(node){
33845     this.node = node;
33846     this.rendered = false;
33847     this.animating = false;
33848     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33849 };
33850
33851 Roo.tree.TreeNodeUI.prototype = {
33852     removeChild : function(node){
33853         if(this.rendered){
33854             this.ctNode.removeChild(node.ui.getEl());
33855         }
33856     },
33857
33858     beforeLoad : function(){
33859          this.addClass("x-tree-node-loading");
33860     },
33861
33862     afterLoad : function(){
33863          this.removeClass("x-tree-node-loading");
33864     },
33865
33866     onTextChange : function(node, text, oldText){
33867         if(this.rendered){
33868             this.textNode.innerHTML = text;
33869         }
33870     },
33871
33872     onDisableChange : function(node, state){
33873         this.disabled = state;
33874         if(state){
33875             this.addClass("x-tree-node-disabled");
33876         }else{
33877             this.removeClass("x-tree-node-disabled");
33878         }
33879     },
33880
33881     onSelectedChange : function(state){
33882         if(state){
33883             this.focus();
33884             this.addClass("x-tree-selected");
33885         }else{
33886             //this.blur();
33887             this.removeClass("x-tree-selected");
33888         }
33889     },
33890
33891     onMove : function(tree, node, oldParent, newParent, index, refNode){
33892         this.childIndent = null;
33893         if(this.rendered){
33894             var targetNode = newParent.ui.getContainer();
33895             if(!targetNode){//target not rendered
33896                 this.holder = document.createElement("div");
33897                 this.holder.appendChild(this.wrap);
33898                 return;
33899             }
33900             var insertBefore = refNode ? refNode.ui.getEl() : null;
33901             if(insertBefore){
33902                 targetNode.insertBefore(this.wrap, insertBefore);
33903             }else{
33904                 targetNode.appendChild(this.wrap);
33905             }
33906             this.node.renderIndent(true);
33907         }
33908     },
33909
33910     addClass : function(cls){
33911         if(this.elNode){
33912             Roo.fly(this.elNode).addClass(cls);
33913         }
33914     },
33915
33916     removeClass : function(cls){
33917         if(this.elNode){
33918             Roo.fly(this.elNode).removeClass(cls);
33919         }
33920     },
33921
33922     remove : function(){
33923         if(this.rendered){
33924             this.holder = document.createElement("div");
33925             this.holder.appendChild(this.wrap);
33926         }
33927     },
33928
33929     fireEvent : function(){
33930         return this.node.fireEvent.apply(this.node, arguments);
33931     },
33932
33933     initEvents : function(){
33934         this.node.on("move", this.onMove, this);
33935         var E = Roo.EventManager;
33936         var a = this.anchor;
33937
33938         var el = Roo.fly(a, '_treeui');
33939
33940         if(Roo.isOpera){ // opera render bug ignores the CSS
33941             el.setStyle("text-decoration", "none");
33942         }
33943
33944         el.on("click", this.onClick, this);
33945         el.on("dblclick", this.onDblClick, this);
33946
33947         if(this.checkbox){
33948             Roo.EventManager.on(this.checkbox,
33949                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33950         }
33951
33952         el.on("contextmenu", this.onContextMenu, this);
33953
33954         var icon = Roo.fly(this.iconNode);
33955         icon.on("click", this.onClick, this);
33956         icon.on("dblclick", this.onDblClick, this);
33957         icon.on("contextmenu", this.onContextMenu, this);
33958         E.on(this.ecNode, "click", this.ecClick, this, true);
33959
33960         if(this.node.disabled){
33961             this.addClass("x-tree-node-disabled");
33962         }
33963         if(this.node.hidden){
33964             this.addClass("x-tree-node-disabled");
33965         }
33966         var ot = this.node.getOwnerTree();
33967         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33968         if(dd && (!this.node.isRoot || ot.rootVisible)){
33969             Roo.dd.Registry.register(this.elNode, {
33970                 node: this.node,
33971                 handles: this.getDDHandles(),
33972                 isHandle: false
33973             });
33974         }
33975     },
33976
33977     getDDHandles : function(){
33978         return [this.iconNode, this.textNode];
33979     },
33980
33981     hide : function(){
33982         if(this.rendered){
33983             this.wrap.style.display = "none";
33984         }
33985     },
33986
33987     show : function(){
33988         if(this.rendered){
33989             this.wrap.style.display = "";
33990         }
33991     },
33992
33993     onContextMenu : function(e){
33994         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33995             e.preventDefault();
33996             this.focus();
33997             this.fireEvent("contextmenu", this.node, e);
33998         }
33999     },
34000
34001     onClick : function(e){
34002         if(this.dropping){
34003             e.stopEvent();
34004             return;
34005         }
34006         if(this.fireEvent("beforeclick", this.node, e) !== false){
34007             if(!this.disabled && this.node.attributes.href){
34008                 this.fireEvent("click", this.node, e);
34009                 return;
34010             }
34011             e.preventDefault();
34012             if(this.disabled){
34013                 return;
34014             }
34015
34016             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
34017                 this.node.toggle();
34018             }
34019
34020             this.fireEvent("click", this.node, e);
34021         }else{
34022             e.stopEvent();
34023         }
34024     },
34025
34026     onDblClick : function(e){
34027         e.preventDefault();
34028         if(this.disabled){
34029             return;
34030         }
34031         if(this.checkbox){
34032             this.toggleCheck();
34033         }
34034         if(!this.animating && this.node.hasChildNodes()){
34035             this.node.toggle();
34036         }
34037         this.fireEvent("dblclick", this.node, e);
34038     },
34039
34040     onCheckChange : function(){
34041         var checked = this.checkbox.checked;
34042         this.node.attributes.checked = checked;
34043         this.fireEvent('checkchange', this.node, checked);
34044     },
34045
34046     ecClick : function(e){
34047         if(!this.animating && this.node.hasChildNodes()){
34048             this.node.toggle();
34049         }
34050     },
34051
34052     startDrop : function(){
34053         this.dropping = true;
34054     },
34055
34056     // delayed drop so the click event doesn't get fired on a drop
34057     endDrop : function(){
34058        setTimeout(function(){
34059            this.dropping = false;
34060        }.createDelegate(this), 50);
34061     },
34062
34063     expand : function(){
34064         this.updateExpandIcon();
34065         this.ctNode.style.display = "";
34066     },
34067
34068     focus : function(){
34069         if(!this.node.preventHScroll){
34070             try{this.anchor.focus();
34071             }catch(e){}
34072         }else if(!Roo.isIE){
34073             try{
34074                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34075                 var l = noscroll.scrollLeft;
34076                 this.anchor.focus();
34077                 noscroll.scrollLeft = l;
34078             }catch(e){}
34079         }
34080     },
34081
34082     toggleCheck : function(value){
34083         var cb = this.checkbox;
34084         if(cb){
34085             cb.checked = (value === undefined ? !cb.checked : value);
34086         }
34087     },
34088
34089     blur : function(){
34090         try{
34091             this.anchor.blur();
34092         }catch(e){}
34093     },
34094
34095     animExpand : function(callback){
34096         var ct = Roo.get(this.ctNode);
34097         ct.stopFx();
34098         if(!this.node.hasChildNodes()){
34099             this.updateExpandIcon();
34100             this.ctNode.style.display = "";
34101             Roo.callback(callback);
34102             return;
34103         }
34104         this.animating = true;
34105         this.updateExpandIcon();
34106
34107         ct.slideIn('t', {
34108            callback : function(){
34109                this.animating = false;
34110                Roo.callback(callback);
34111             },
34112             scope: this,
34113             duration: this.node.ownerTree.duration || .25
34114         });
34115     },
34116
34117     highlight : function(){
34118         var tree = this.node.getOwnerTree();
34119         Roo.fly(this.wrap).highlight(
34120             tree.hlColor || "C3DAF9",
34121             {endColor: tree.hlBaseColor}
34122         );
34123     },
34124
34125     collapse : function(){
34126         this.updateExpandIcon();
34127         this.ctNode.style.display = "none";
34128     },
34129
34130     animCollapse : function(callback){
34131         var ct = Roo.get(this.ctNode);
34132         ct.enableDisplayMode('block');
34133         ct.stopFx();
34134
34135         this.animating = true;
34136         this.updateExpandIcon();
34137
34138         ct.slideOut('t', {
34139             callback : function(){
34140                this.animating = false;
34141                Roo.callback(callback);
34142             },
34143             scope: this,
34144             duration: this.node.ownerTree.duration || .25
34145         });
34146     },
34147
34148     getContainer : function(){
34149         return this.ctNode;
34150     },
34151
34152     getEl : function(){
34153         return this.wrap;
34154     },
34155
34156     appendDDGhost : function(ghostNode){
34157         ghostNode.appendChild(this.elNode.cloneNode(true));
34158     },
34159
34160     getDDRepairXY : function(){
34161         return Roo.lib.Dom.getXY(this.iconNode);
34162     },
34163
34164     onRender : function(){
34165         this.render();
34166     },
34167
34168     render : function(bulkRender){
34169         var n = this.node, a = n.attributes;
34170         var targetNode = n.parentNode ?
34171               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34172
34173         if(!this.rendered){
34174             this.rendered = true;
34175
34176             this.renderElements(n, a, targetNode, bulkRender);
34177
34178             if(a.qtip){
34179                if(this.textNode.setAttributeNS){
34180                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34181                    if(a.qtipTitle){
34182                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34183                    }
34184                }else{
34185                    this.textNode.setAttribute("ext:qtip", a.qtip);
34186                    if(a.qtipTitle){
34187                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34188                    }
34189                }
34190             }else if(a.qtipCfg){
34191                 a.qtipCfg.target = Roo.id(this.textNode);
34192                 Roo.QuickTips.register(a.qtipCfg);
34193             }
34194             this.initEvents();
34195             if(!this.node.expanded){
34196                 this.updateExpandIcon();
34197             }
34198         }else{
34199             if(bulkRender === true) {
34200                 targetNode.appendChild(this.wrap);
34201             }
34202         }
34203     },
34204
34205     renderElements : function(n, a, targetNode, bulkRender)
34206     {
34207         // add some indent caching, this helps performance when rendering a large tree
34208         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34209         var t = n.getOwnerTree();
34210         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34211         if (typeof(n.attributes.html) != 'undefined') {
34212             txt = n.attributes.html;
34213         }
34214         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34215         var cb = typeof a.checked == 'boolean';
34216         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34217         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34218             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34219             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34220             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34221             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34222             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34223              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34224                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34225             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34226             "</li>"];
34227
34228         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34229             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34230                                 n.nextSibling.ui.getEl(), buf.join(""));
34231         }else{
34232             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34233         }
34234
34235         this.elNode = this.wrap.childNodes[0];
34236         this.ctNode = this.wrap.childNodes[1];
34237         var cs = this.elNode.childNodes;
34238         this.indentNode = cs[0];
34239         this.ecNode = cs[1];
34240         this.iconNode = cs[2];
34241         var index = 3;
34242         if(cb){
34243             this.checkbox = cs[3];
34244             index++;
34245         }
34246         this.anchor = cs[index];
34247         this.textNode = cs[index].firstChild;
34248     },
34249
34250     getAnchor : function(){
34251         return this.anchor;
34252     },
34253
34254     getTextEl : function(){
34255         return this.textNode;
34256     },
34257
34258     getIconEl : function(){
34259         return this.iconNode;
34260     },
34261
34262     isChecked : function(){
34263         return this.checkbox ? this.checkbox.checked : false;
34264     },
34265
34266     updateExpandIcon : function(){
34267         if(this.rendered){
34268             var n = this.node, c1, c2;
34269             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34270             var hasChild = n.hasChildNodes();
34271             if(hasChild){
34272                 if(n.expanded){
34273                     cls += "-minus";
34274                     c1 = "x-tree-node-collapsed";
34275                     c2 = "x-tree-node-expanded";
34276                 }else{
34277                     cls += "-plus";
34278                     c1 = "x-tree-node-expanded";
34279                     c2 = "x-tree-node-collapsed";
34280                 }
34281                 if(this.wasLeaf){
34282                     this.removeClass("x-tree-node-leaf");
34283                     this.wasLeaf = false;
34284                 }
34285                 if(this.c1 != c1 || this.c2 != c2){
34286                     Roo.fly(this.elNode).replaceClass(c1, c2);
34287                     this.c1 = c1; this.c2 = c2;
34288                 }
34289             }else{
34290                 // this changes non-leafs into leafs if they have no children.
34291                 // it's not very rational behaviour..
34292                 
34293                 if(!this.wasLeaf && this.node.leaf){
34294                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34295                     delete this.c1;
34296                     delete this.c2;
34297                     this.wasLeaf = true;
34298                 }
34299             }
34300             var ecc = "x-tree-ec-icon "+cls;
34301             if(this.ecc != ecc){
34302                 this.ecNode.className = ecc;
34303                 this.ecc = ecc;
34304             }
34305         }
34306     },
34307
34308     getChildIndent : function(){
34309         if(!this.childIndent){
34310             var buf = [];
34311             var p = this.node;
34312             while(p){
34313                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34314                     if(!p.isLast()) {
34315                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34316                     } else {
34317                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34318                     }
34319                 }
34320                 p = p.parentNode;
34321             }
34322             this.childIndent = buf.join("");
34323         }
34324         return this.childIndent;
34325     },
34326
34327     renderIndent : function(){
34328         if(this.rendered){
34329             var indent = "";
34330             var p = this.node.parentNode;
34331             if(p){
34332                 indent = p.ui.getChildIndent();
34333             }
34334             if(this.indentMarkup != indent){ // don't rerender if not required
34335                 this.indentNode.innerHTML = indent;
34336                 this.indentMarkup = indent;
34337             }
34338             this.updateExpandIcon();
34339         }
34340     }
34341 };
34342
34343 Roo.tree.RootTreeNodeUI = function(){
34344     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34345 };
34346 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34347     render : function(){
34348         if(!this.rendered){
34349             var targetNode = this.node.ownerTree.innerCt.dom;
34350             this.node.expanded = true;
34351             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34352             this.wrap = this.ctNode = targetNode.firstChild;
34353         }
34354     },
34355     collapse : function(){
34356     },
34357     expand : function(){
34358     }
34359 });/*
34360  * Based on:
34361  * Ext JS Library 1.1.1
34362  * Copyright(c) 2006-2007, Ext JS, LLC.
34363  *
34364  * Originally Released Under LGPL - original licence link has changed is not relivant.
34365  *
34366  * Fork - LGPL
34367  * <script type="text/javascript">
34368  */
34369 /**
34370  * @class Roo.tree.TreeLoader
34371  * @extends Roo.util.Observable
34372  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34373  * nodes from a specified URL. The response must be a javascript Array definition
34374  * who's elements are node definition objects. eg:
34375  * <pre><code>
34376 {  success : true,
34377    data :      [
34378    
34379     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34380     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34381     ]
34382 }
34383
34384
34385 </code></pre>
34386  * <br><br>
34387  * The old style respose with just an array is still supported, but not recommended.
34388  * <br><br>
34389  *
34390  * A server request is sent, and child nodes are loaded only when a node is expanded.
34391  * The loading node's id is passed to the server under the parameter name "node" to
34392  * enable the server to produce the correct child nodes.
34393  * <br><br>
34394  * To pass extra parameters, an event handler may be attached to the "beforeload"
34395  * event, and the parameters specified in the TreeLoader's baseParams property:
34396  * <pre><code>
34397     myTreeLoader.on("beforeload", function(treeLoader, node) {
34398         this.baseParams.category = node.attributes.category;
34399     }, this);
34400 </code></pre><
34401  * This would pass an HTTP parameter called "category" to the server containing
34402  * the value of the Node's "category" attribute.
34403  * @constructor
34404  * Creates a new Treeloader.
34405  * @param {Object} config A config object containing config properties.
34406  */
34407 Roo.tree.TreeLoader = function(config){
34408     this.baseParams = {};
34409     this.requestMethod = "POST";
34410     Roo.apply(this, config);
34411
34412     this.addEvents({
34413     
34414         /**
34415          * @event beforeload
34416          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34417          * @param {Object} This TreeLoader object.
34418          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34419          * @param {Object} callback The callback function specified in the {@link #load} call.
34420          */
34421         beforeload : true,
34422         /**
34423          * @event load
34424          * Fires when the node has been successfuly loaded.
34425          * @param {Object} This TreeLoader object.
34426          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34427          * @param {Object} response The response object containing the data from the server.
34428          */
34429         load : true,
34430         /**
34431          * @event loadexception
34432          * Fires if the network request failed.
34433          * @param {Object} This TreeLoader object.
34434          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34435          * @param {Object} response The response object containing the data from the server.
34436          */
34437         loadexception : true,
34438         /**
34439          * @event create
34440          * Fires before a node is created, enabling you to return custom Node types 
34441          * @param {Object} This TreeLoader object.
34442          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34443          */
34444         create : true
34445     });
34446
34447     Roo.tree.TreeLoader.superclass.constructor.call(this);
34448 };
34449
34450 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34451     /**
34452     * @cfg {String} dataUrl The URL from which to request a Json string which
34453     * specifies an array of node definition object representing the child nodes
34454     * to be loaded.
34455     */
34456     /**
34457     * @cfg {String} requestMethod either GET or POST
34458     * defaults to POST (due to BC)
34459     * to be loaded.
34460     */
34461     /**
34462     * @cfg {Object} baseParams (optional) An object containing properties which
34463     * specify HTTP parameters to be passed to each request for child nodes.
34464     */
34465     /**
34466     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34467     * created by this loader. If the attributes sent by the server have an attribute in this object,
34468     * they take priority.
34469     */
34470     /**
34471     * @cfg {Object} uiProviders (optional) An object containing properties which
34472     * 
34473     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34474     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34475     * <i>uiProvider</i> attribute of a returned child node is a string rather
34476     * than a reference to a TreeNodeUI implementation, this that string value
34477     * is used as a property name in the uiProviders object. You can define the provider named
34478     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34479     */
34480     uiProviders : {},
34481
34482     /**
34483     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34484     * child nodes before loading.
34485     */
34486     clearOnLoad : true,
34487
34488     /**
34489     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34490     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34491     * Grid query { data : [ .....] }
34492     */
34493     
34494     root : false,
34495      /**
34496     * @cfg {String} queryParam (optional) 
34497     * Name of the query as it will be passed on the querystring (defaults to 'node')
34498     * eg. the request will be ?node=[id]
34499     */
34500     
34501     
34502     queryParam: false,
34503     
34504     /**
34505      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34506      * This is called automatically when a node is expanded, but may be used to reload
34507      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34508      * @param {Roo.tree.TreeNode} node
34509      * @param {Function} callback
34510      */
34511     load : function(node, callback){
34512         if(this.clearOnLoad){
34513             while(node.firstChild){
34514                 node.removeChild(node.firstChild);
34515             }
34516         }
34517         if(node.attributes.children){ // preloaded json children
34518             var cs = node.attributes.children;
34519             for(var i = 0, len = cs.length; i < len; i++){
34520                 node.appendChild(this.createNode(cs[i]));
34521             }
34522             if(typeof callback == "function"){
34523                 callback();
34524             }
34525         }else if(this.dataUrl){
34526             this.requestData(node, callback);
34527         }
34528     },
34529
34530     getParams: function(node){
34531         var buf = [], bp = this.baseParams;
34532         for(var key in bp){
34533             if(typeof bp[key] != "function"){
34534                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34535             }
34536         }
34537         var n = this.queryParam === false ? 'node' : this.queryParam;
34538         buf.push(n + "=", encodeURIComponent(node.id));
34539         return buf.join("");
34540     },
34541
34542     requestData : function(node, callback){
34543         if(this.fireEvent("beforeload", this, node, callback) !== false){
34544             this.transId = Roo.Ajax.request({
34545                 method:this.requestMethod,
34546                 url: this.dataUrl||this.url,
34547                 success: this.handleResponse,
34548                 failure: this.handleFailure,
34549                 scope: this,
34550                 argument: {callback: callback, node: node},
34551                 params: this.getParams(node)
34552             });
34553         }else{
34554             // if the load is cancelled, make sure we notify
34555             // the node that we are done
34556             if(typeof callback == "function"){
34557                 callback();
34558             }
34559         }
34560     },
34561
34562     isLoading : function(){
34563         return this.transId ? true : false;
34564     },
34565
34566     abort : function(){
34567         if(this.isLoading()){
34568             Roo.Ajax.abort(this.transId);
34569         }
34570     },
34571
34572     // private
34573     createNode : function(attr)
34574     {
34575         // apply baseAttrs, nice idea Corey!
34576         if(this.baseAttrs){
34577             Roo.applyIf(attr, this.baseAttrs);
34578         }
34579         if(this.applyLoader !== false){
34580             attr.loader = this;
34581         }
34582         // uiProvider = depreciated..
34583         
34584         if(typeof(attr.uiProvider) == 'string'){
34585            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34586                 /**  eval:var:attr */ eval(attr.uiProvider);
34587         }
34588         if(typeof(this.uiProviders['default']) != 'undefined') {
34589             attr.uiProvider = this.uiProviders['default'];
34590         }
34591         
34592         this.fireEvent('create', this, attr);
34593         
34594         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34595         return(attr.leaf ?
34596                         new Roo.tree.TreeNode(attr) :
34597                         new Roo.tree.AsyncTreeNode(attr));
34598     },
34599
34600     processResponse : function(response, node, callback)
34601     {
34602         var json = response.responseText;
34603         try {
34604             
34605             var o = Roo.decode(json);
34606             
34607             if (this.root === false && typeof(o.success) != undefined) {
34608                 this.root = 'data'; // the default behaviour for list like data..
34609                 }
34610                 
34611             if (this.root !== false &&  !o.success) {
34612                 // it's a failure condition.
34613                 var a = response.argument;
34614                 this.fireEvent("loadexception", this, a.node, response);
34615                 Roo.log("Load failed - should have a handler really");
34616                 return;
34617             }
34618             
34619             
34620             
34621             if (this.root !== false) {
34622                  o = o[this.root];
34623             }
34624             
34625             for(var i = 0, len = o.length; i < len; i++){
34626                 var n = this.createNode(o[i]);
34627                 if(n){
34628                     node.appendChild(n);
34629                 }
34630             }
34631             if(typeof callback == "function"){
34632                 callback(this, node);
34633             }
34634         }catch(e){
34635             this.handleFailure(response);
34636         }
34637     },
34638
34639     handleResponse : function(response){
34640         this.transId = false;
34641         var a = response.argument;
34642         this.processResponse(response, a.node, a.callback);
34643         this.fireEvent("load", this, a.node, response);
34644     },
34645
34646     handleFailure : function(response)
34647     {
34648         // should handle failure better..
34649         this.transId = false;
34650         var a = response.argument;
34651         this.fireEvent("loadexception", this, a.node, response);
34652         if(typeof a.callback == "function"){
34653             a.callback(this, a.node);
34654         }
34655     }
34656 });/*
34657  * Based on:
34658  * Ext JS Library 1.1.1
34659  * Copyright(c) 2006-2007, Ext JS, LLC.
34660  *
34661  * Originally Released Under LGPL - original licence link has changed is not relivant.
34662  *
34663  * Fork - LGPL
34664  * <script type="text/javascript">
34665  */
34666
34667 /**
34668 * @class Roo.tree.TreeFilter
34669 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34670 * @param {TreePanel} tree
34671 * @param {Object} config (optional)
34672  */
34673 Roo.tree.TreeFilter = function(tree, config){
34674     this.tree = tree;
34675     this.filtered = {};
34676     Roo.apply(this, config);
34677 };
34678
34679 Roo.tree.TreeFilter.prototype = {
34680     clearBlank:false,
34681     reverse:false,
34682     autoClear:false,
34683     remove:false,
34684
34685      /**
34686      * Filter the data by a specific attribute.
34687      * @param {String/RegExp} value Either string that the attribute value
34688      * should start with or a RegExp to test against the attribute
34689      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34690      * @param {TreeNode} startNode (optional) The node to start the filter at.
34691      */
34692     filter : function(value, attr, startNode){
34693         attr = attr || "text";
34694         var f;
34695         if(typeof value == "string"){
34696             var vlen = value.length;
34697             // auto clear empty filter
34698             if(vlen == 0 && this.clearBlank){
34699                 this.clear();
34700                 return;
34701             }
34702             value = value.toLowerCase();
34703             f = function(n){
34704                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34705             };
34706         }else if(value.exec){ // regex?
34707             f = function(n){
34708                 return value.test(n.attributes[attr]);
34709             };
34710         }else{
34711             throw 'Illegal filter type, must be string or regex';
34712         }
34713         this.filterBy(f, null, startNode);
34714         },
34715
34716     /**
34717      * Filter by a function. The passed function will be called with each
34718      * node in the tree (or from the startNode). If the function returns true, the node is kept
34719      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34720      * @param {Function} fn The filter function
34721      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34722      */
34723     filterBy : function(fn, scope, startNode){
34724         startNode = startNode || this.tree.root;
34725         if(this.autoClear){
34726             this.clear();
34727         }
34728         var af = this.filtered, rv = this.reverse;
34729         var f = function(n){
34730             if(n == startNode){
34731                 return true;
34732             }
34733             if(af[n.id]){
34734                 return false;
34735             }
34736             var m = fn.call(scope || n, n);
34737             if(!m || rv){
34738                 af[n.id] = n;
34739                 n.ui.hide();
34740                 return false;
34741             }
34742             return true;
34743         };
34744         startNode.cascade(f);
34745         if(this.remove){
34746            for(var id in af){
34747                if(typeof id != "function"){
34748                    var n = af[id];
34749                    if(n && n.parentNode){
34750                        n.parentNode.removeChild(n);
34751                    }
34752                }
34753            }
34754         }
34755     },
34756
34757     /**
34758      * Clears the current filter. Note: with the "remove" option
34759      * set a filter cannot be cleared.
34760      */
34761     clear : function(){
34762         var t = this.tree;
34763         var af = this.filtered;
34764         for(var id in af){
34765             if(typeof id != "function"){
34766                 var n = af[id];
34767                 if(n){
34768                     n.ui.show();
34769                 }
34770             }
34771         }
34772         this.filtered = {};
34773     }
34774 };
34775 /*
34776  * Based on:
34777  * Ext JS Library 1.1.1
34778  * Copyright(c) 2006-2007, Ext JS, LLC.
34779  *
34780  * Originally Released Under LGPL - original licence link has changed is not relivant.
34781  *
34782  * Fork - LGPL
34783  * <script type="text/javascript">
34784  */
34785  
34786
34787 /**
34788  * @class Roo.tree.TreeSorter
34789  * Provides sorting of nodes in a TreePanel
34790  * 
34791  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34792  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34793  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34794  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34795  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34796  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34797  * @constructor
34798  * @param {TreePanel} tree
34799  * @param {Object} config
34800  */
34801 Roo.tree.TreeSorter = function(tree, config){
34802     Roo.apply(this, config);
34803     tree.on("beforechildrenrendered", this.doSort, this);
34804     tree.on("append", this.updateSort, this);
34805     tree.on("insert", this.updateSort, this);
34806     
34807     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34808     var p = this.property || "text";
34809     var sortType = this.sortType;
34810     var fs = this.folderSort;
34811     var cs = this.caseSensitive === true;
34812     var leafAttr = this.leafAttr || 'leaf';
34813
34814     this.sortFn = function(n1, n2){
34815         if(fs){
34816             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34817                 return 1;
34818             }
34819             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34820                 return -1;
34821             }
34822         }
34823         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34824         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34825         if(v1 < v2){
34826                         return dsc ? +1 : -1;
34827                 }else if(v1 > v2){
34828                         return dsc ? -1 : +1;
34829         }else{
34830                 return 0;
34831         }
34832     };
34833 };
34834
34835 Roo.tree.TreeSorter.prototype = {
34836     doSort : function(node){
34837         node.sort(this.sortFn);
34838     },
34839     
34840     compareNodes : function(n1, n2){
34841         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34842     },
34843     
34844     updateSort : function(tree, node){
34845         if(node.childrenRendered){
34846             this.doSort.defer(1, this, [node]);
34847         }
34848     }
34849 };/*
34850  * Based on:
34851  * Ext JS Library 1.1.1
34852  * Copyright(c) 2006-2007, Ext JS, LLC.
34853  *
34854  * Originally Released Under LGPL - original licence link has changed is not relivant.
34855  *
34856  * Fork - LGPL
34857  * <script type="text/javascript">
34858  */
34859
34860 if(Roo.dd.DropZone){
34861     
34862 Roo.tree.TreeDropZone = function(tree, config){
34863     this.allowParentInsert = false;
34864     this.allowContainerDrop = false;
34865     this.appendOnly = false;
34866     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34867     this.tree = tree;
34868     this.lastInsertClass = "x-tree-no-status";
34869     this.dragOverData = {};
34870 };
34871
34872 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34873     ddGroup : "TreeDD",
34874     scroll:  true,
34875     
34876     expandDelay : 1000,
34877     
34878     expandNode : function(node){
34879         if(node.hasChildNodes() && !node.isExpanded()){
34880             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34881         }
34882     },
34883     
34884     queueExpand : function(node){
34885         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34886     },
34887     
34888     cancelExpand : function(){
34889         if(this.expandProcId){
34890             clearTimeout(this.expandProcId);
34891             this.expandProcId = false;
34892         }
34893     },
34894     
34895     isValidDropPoint : function(n, pt, dd, e, data){
34896         if(!n || !data){ return false; }
34897         var targetNode = n.node;
34898         var dropNode = data.node;
34899         // default drop rules
34900         if(!(targetNode && targetNode.isTarget && pt)){
34901             return false;
34902         }
34903         if(pt == "append" && targetNode.allowChildren === false){
34904             return false;
34905         }
34906         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34907             return false;
34908         }
34909         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34910             return false;
34911         }
34912         // reuse the object
34913         var overEvent = this.dragOverData;
34914         overEvent.tree = this.tree;
34915         overEvent.target = targetNode;
34916         overEvent.data = data;
34917         overEvent.point = pt;
34918         overEvent.source = dd;
34919         overEvent.rawEvent = e;
34920         overEvent.dropNode = dropNode;
34921         overEvent.cancel = false;  
34922         var result = this.tree.fireEvent("nodedragover", overEvent);
34923         return overEvent.cancel === false && result !== false;
34924     },
34925     
34926     getDropPoint : function(e, n, dd)
34927     {
34928         var tn = n.node;
34929         if(tn.isRoot){
34930             return tn.allowChildren !== false ? "append" : false; // always append for root
34931         }
34932         var dragEl = n.ddel;
34933         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34934         var y = Roo.lib.Event.getPageY(e);
34935         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34936         
34937         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34938         var noAppend = tn.allowChildren === false;
34939         if(this.appendOnly || tn.parentNode.allowChildren === false){
34940             return noAppend ? false : "append";
34941         }
34942         var noBelow = false;
34943         if(!this.allowParentInsert){
34944             noBelow = tn.hasChildNodes() && tn.isExpanded();
34945         }
34946         var q = (b - t) / (noAppend ? 2 : 3);
34947         if(y >= t && y < (t + q)){
34948             return "above";
34949         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34950             return "below";
34951         }else{
34952             return "append";
34953         }
34954     },
34955     
34956     onNodeEnter : function(n, dd, e, data)
34957     {
34958         this.cancelExpand();
34959     },
34960     
34961     onNodeOver : function(n, dd, e, data)
34962     {
34963        
34964         var pt = this.getDropPoint(e, n, dd);
34965         var node = n.node;
34966         
34967         // auto node expand check
34968         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34969             this.queueExpand(node);
34970         }else if(pt != "append"){
34971             this.cancelExpand();
34972         }
34973         
34974         // set the insert point style on the target node
34975         var returnCls = this.dropNotAllowed;
34976         if(this.isValidDropPoint(n, pt, dd, e, data)){
34977            if(pt){
34978                var el = n.ddel;
34979                var cls;
34980                if(pt == "above"){
34981                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34982                    cls = "x-tree-drag-insert-above";
34983                }else if(pt == "below"){
34984                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34985                    cls = "x-tree-drag-insert-below";
34986                }else{
34987                    returnCls = "x-tree-drop-ok-append";
34988                    cls = "x-tree-drag-append";
34989                }
34990                if(this.lastInsertClass != cls){
34991                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34992                    this.lastInsertClass = cls;
34993                }
34994            }
34995        }
34996        return returnCls;
34997     },
34998     
34999     onNodeOut : function(n, dd, e, data){
35000         
35001         this.cancelExpand();
35002         this.removeDropIndicators(n);
35003     },
35004     
35005     onNodeDrop : function(n, dd, e, data){
35006         var point = this.getDropPoint(e, n, dd);
35007         var targetNode = n.node;
35008         targetNode.ui.startDrop();
35009         if(!this.isValidDropPoint(n, point, dd, e, data)){
35010             targetNode.ui.endDrop();
35011             return false;
35012         }
35013         // first try to find the drop node
35014         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
35015         var dropEvent = {
35016             tree : this.tree,
35017             target: targetNode,
35018             data: data,
35019             point: point,
35020             source: dd,
35021             rawEvent: e,
35022             dropNode: dropNode,
35023             cancel: !dropNode   
35024         };
35025         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
35026         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
35027             targetNode.ui.endDrop();
35028             return false;
35029         }
35030         // allow target changing
35031         targetNode = dropEvent.target;
35032         if(point == "append" && !targetNode.isExpanded()){
35033             targetNode.expand(false, null, function(){
35034                 this.completeDrop(dropEvent);
35035             }.createDelegate(this));
35036         }else{
35037             this.completeDrop(dropEvent);
35038         }
35039         return true;
35040     },
35041     
35042     completeDrop : function(de){
35043         var ns = de.dropNode, p = de.point, t = de.target;
35044         if(!(ns instanceof Array)){
35045             ns = [ns];
35046         }
35047         var n;
35048         for(var i = 0, len = ns.length; i < len; i++){
35049             n = ns[i];
35050             if(p == "above"){
35051                 t.parentNode.insertBefore(n, t);
35052             }else if(p == "below"){
35053                 t.parentNode.insertBefore(n, t.nextSibling);
35054             }else{
35055                 t.appendChild(n);
35056             }
35057         }
35058         n.ui.focus();
35059         if(this.tree.hlDrop){
35060             n.ui.highlight();
35061         }
35062         t.ui.endDrop();
35063         this.tree.fireEvent("nodedrop", de);
35064     },
35065     
35066     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35067         if(this.tree.hlDrop){
35068             dropNode.ui.focus();
35069             dropNode.ui.highlight();
35070         }
35071         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35072     },
35073     
35074     getTree : function(){
35075         return this.tree;
35076     },
35077     
35078     removeDropIndicators : function(n){
35079         if(n && n.ddel){
35080             var el = n.ddel;
35081             Roo.fly(el).removeClass([
35082                     "x-tree-drag-insert-above",
35083                     "x-tree-drag-insert-below",
35084                     "x-tree-drag-append"]);
35085             this.lastInsertClass = "_noclass";
35086         }
35087     },
35088     
35089     beforeDragDrop : function(target, e, id){
35090         this.cancelExpand();
35091         return true;
35092     },
35093     
35094     afterRepair : function(data){
35095         if(data && Roo.enableFx){
35096             data.node.ui.highlight();
35097         }
35098         this.hideProxy();
35099     } 
35100     
35101 });
35102
35103 }
35104 /*
35105  * Based on:
35106  * Ext JS Library 1.1.1
35107  * Copyright(c) 2006-2007, Ext JS, LLC.
35108  *
35109  * Originally Released Under LGPL - original licence link has changed is not relivant.
35110  *
35111  * Fork - LGPL
35112  * <script type="text/javascript">
35113  */
35114  
35115
35116 if(Roo.dd.DragZone){
35117 Roo.tree.TreeDragZone = function(tree, config){
35118     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35119     this.tree = tree;
35120 };
35121
35122 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35123     ddGroup : "TreeDD",
35124    
35125     onBeforeDrag : function(data, e){
35126         var n = data.node;
35127         return n && n.draggable && !n.disabled;
35128     },
35129      
35130     
35131     onInitDrag : function(e){
35132         var data = this.dragData;
35133         this.tree.getSelectionModel().select(data.node);
35134         this.proxy.update("");
35135         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35136         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35137     },
35138     
35139     getRepairXY : function(e, data){
35140         return data.node.ui.getDDRepairXY();
35141     },
35142     
35143     onEndDrag : function(data, e){
35144         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35145         
35146         
35147     },
35148     
35149     onValidDrop : function(dd, e, id){
35150         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35151         this.hideProxy();
35152     },
35153     
35154     beforeInvalidDrop : function(e, id){
35155         // this scrolls the original position back into view
35156         var sm = this.tree.getSelectionModel();
35157         sm.clearSelections();
35158         sm.select(this.dragData.node);
35159     }
35160 });
35161 }/*
35162  * Based on:
35163  * Ext JS Library 1.1.1
35164  * Copyright(c) 2006-2007, Ext JS, LLC.
35165  *
35166  * Originally Released Under LGPL - original licence link has changed is not relivant.
35167  *
35168  * Fork - LGPL
35169  * <script type="text/javascript">
35170  */
35171 /**
35172  * @class Roo.tree.TreeEditor
35173  * @extends Roo.Editor
35174  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35175  * as the editor field.
35176  * @constructor
35177  * @param {Object} config (used to be the tree panel.)
35178  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35179  * 
35180  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35181  * @cfg {Roo.form.TextField|Object} field The field configuration
35182  *
35183  * 
35184  */
35185 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35186     var tree = config;
35187     var field;
35188     if (oldconfig) { // old style..
35189         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35190     } else {
35191         // new style..
35192         tree = config.tree;
35193         config.field = config.field  || {};
35194         config.field.xtype = 'TextField';
35195         field = Roo.factory(config.field, Roo.form);
35196     }
35197     config = config || {};
35198     
35199     
35200     this.addEvents({
35201         /**
35202          * @event beforenodeedit
35203          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35204          * false from the handler of this event.
35205          * @param {Editor} this
35206          * @param {Roo.tree.Node} node 
35207          */
35208         "beforenodeedit" : true
35209     });
35210     
35211     //Roo.log(config);
35212     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35213
35214     this.tree = tree;
35215
35216     tree.on('beforeclick', this.beforeNodeClick, this);
35217     tree.getTreeEl().on('mousedown', this.hide, this);
35218     this.on('complete', this.updateNode, this);
35219     this.on('beforestartedit', this.fitToTree, this);
35220     this.on('startedit', this.bindScroll, this, {delay:10});
35221     this.on('specialkey', this.onSpecialKey, this);
35222 };
35223
35224 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35225     /**
35226      * @cfg {String} alignment
35227      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35228      */
35229     alignment: "l-l",
35230     // inherit
35231     autoSize: false,
35232     /**
35233      * @cfg {Boolean} hideEl
35234      * True to hide the bound element while the editor is displayed (defaults to false)
35235      */
35236     hideEl : false,
35237     /**
35238      * @cfg {String} cls
35239      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35240      */
35241     cls: "x-small-editor x-tree-editor",
35242     /**
35243      * @cfg {Boolean} shim
35244      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35245      */
35246     shim:false,
35247     // inherit
35248     shadow:"frame",
35249     /**
35250      * @cfg {Number} maxWidth
35251      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35252      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35253      * scroll and client offsets into account prior to each edit.
35254      */
35255     maxWidth: 250,
35256
35257     editDelay : 350,
35258
35259     // private
35260     fitToTree : function(ed, el){
35261         var td = this.tree.getTreeEl().dom, nd = el.dom;
35262         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35263             td.scrollLeft = nd.offsetLeft;
35264         }
35265         var w = Math.min(
35266                 this.maxWidth,
35267                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35268         this.setSize(w, '');
35269         
35270         return this.fireEvent('beforenodeedit', this, this.editNode);
35271         
35272     },
35273
35274     // private
35275     triggerEdit : function(node){
35276         this.completeEdit();
35277         this.editNode = node;
35278         this.startEdit(node.ui.textNode, node.text);
35279     },
35280
35281     // private
35282     bindScroll : function(){
35283         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35284     },
35285
35286     // private
35287     beforeNodeClick : function(node, e){
35288         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35289         this.lastClick = new Date();
35290         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35291             e.stopEvent();
35292             this.triggerEdit(node);
35293             return false;
35294         }
35295         return true;
35296     },
35297
35298     // private
35299     updateNode : function(ed, value){
35300         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35301         this.editNode.setText(value);
35302     },
35303
35304     // private
35305     onHide : function(){
35306         Roo.tree.TreeEditor.superclass.onHide.call(this);
35307         if(this.editNode){
35308             this.editNode.ui.focus();
35309         }
35310     },
35311
35312     // private
35313     onSpecialKey : function(field, e){
35314         var k = e.getKey();
35315         if(k == e.ESC){
35316             e.stopEvent();
35317             this.cancelEdit();
35318         }else if(k == e.ENTER && !e.hasModifier()){
35319             e.stopEvent();
35320             this.completeEdit();
35321         }
35322     }
35323 });//<Script type="text/javascript">
35324 /*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335 /**
35336  * Not documented??? - probably should be...
35337  */
35338
35339 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35340     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35341     
35342     renderElements : function(n, a, targetNode, bulkRender){
35343         //consel.log("renderElements?");
35344         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35345
35346         var t = n.getOwnerTree();
35347         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35348         
35349         var cols = t.columns;
35350         var bw = t.borderWidth;
35351         var c = cols[0];
35352         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35353          var cb = typeof a.checked == "boolean";
35354         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35355         var colcls = 'x-t-' + tid + '-c0';
35356         var buf = [
35357             '<li class="x-tree-node">',
35358             
35359                 
35360                 '<div class="x-tree-node-el ', a.cls,'">',
35361                     // extran...
35362                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35363                 
35364                 
35365                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35366                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35367                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35368                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35369                            (a.iconCls ? ' '+a.iconCls : ''),
35370                            '" unselectable="on" />',
35371                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35372                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35373                              
35374                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35375                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35376                             '<span unselectable="on" qtip="' + tx + '">',
35377                              tx,
35378                              '</span></a>' ,
35379                     '</div>',
35380                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35381                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35382                  ];
35383         for(var i = 1, len = cols.length; i < len; i++){
35384             c = cols[i];
35385             colcls = 'x-t-' + tid + '-c' +i;
35386             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35387             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35388                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35389                       "</div>");
35390          }
35391          
35392          buf.push(
35393             '</a>',
35394             '<div class="x-clear"></div></div>',
35395             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35396             "</li>");
35397         
35398         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35399             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35400                                 n.nextSibling.ui.getEl(), buf.join(""));
35401         }else{
35402             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35403         }
35404         var el = this.wrap.firstChild;
35405         this.elRow = el;
35406         this.elNode = el.firstChild;
35407         this.ranchor = el.childNodes[1];
35408         this.ctNode = this.wrap.childNodes[1];
35409         var cs = el.firstChild.childNodes;
35410         this.indentNode = cs[0];
35411         this.ecNode = cs[1];
35412         this.iconNode = cs[2];
35413         var index = 3;
35414         if(cb){
35415             this.checkbox = cs[3];
35416             index++;
35417         }
35418         this.anchor = cs[index];
35419         
35420         this.textNode = cs[index].firstChild;
35421         
35422         //el.on("click", this.onClick, this);
35423         //el.on("dblclick", this.onDblClick, this);
35424         
35425         
35426        // console.log(this);
35427     },
35428     initEvents : function(){
35429         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35430         
35431             
35432         var a = this.ranchor;
35433
35434         var el = Roo.get(a);
35435
35436         if(Roo.isOpera){ // opera render bug ignores the CSS
35437             el.setStyle("text-decoration", "none");
35438         }
35439
35440         el.on("click", this.onClick, this);
35441         el.on("dblclick", this.onDblClick, this);
35442         el.on("contextmenu", this.onContextMenu, this);
35443         
35444     },
35445     
35446     /*onSelectedChange : function(state){
35447         if(state){
35448             this.focus();
35449             this.addClass("x-tree-selected");
35450         }else{
35451             //this.blur();
35452             this.removeClass("x-tree-selected");
35453         }
35454     },*/
35455     addClass : function(cls){
35456         if(this.elRow){
35457             Roo.fly(this.elRow).addClass(cls);
35458         }
35459         
35460     },
35461     
35462     
35463     removeClass : function(cls){
35464         if(this.elRow){
35465             Roo.fly(this.elRow).removeClass(cls);
35466         }
35467     }
35468
35469     
35470     
35471 });//<Script type="text/javascript">
35472
35473 /*
35474  * Based on:
35475  * Ext JS Library 1.1.1
35476  * Copyright(c) 2006-2007, Ext JS, LLC.
35477  *
35478  * Originally Released Under LGPL - original licence link has changed is not relivant.
35479  *
35480  * Fork - LGPL
35481  * <script type="text/javascript">
35482  */
35483  
35484
35485 /**
35486  * @class Roo.tree.ColumnTree
35487  * @extends Roo.data.TreePanel
35488  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35489  * @cfg {int} borderWidth  compined right/left border allowance
35490  * @constructor
35491  * @param {String/HTMLElement/Element} el The container element
35492  * @param {Object} config
35493  */
35494 Roo.tree.ColumnTree =  function(el, config)
35495 {
35496    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35497    this.addEvents({
35498         /**
35499         * @event resize
35500         * Fire this event on a container when it resizes
35501         * @param {int} w Width
35502         * @param {int} h Height
35503         */
35504        "resize" : true
35505     });
35506     this.on('resize', this.onResize, this);
35507 };
35508
35509 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35510     //lines:false,
35511     
35512     
35513     borderWidth: Roo.isBorderBox ? 0 : 2, 
35514     headEls : false,
35515     
35516     render : function(){
35517         // add the header.....
35518        
35519         Roo.tree.ColumnTree.superclass.render.apply(this);
35520         
35521         this.el.addClass('x-column-tree');
35522         
35523         this.headers = this.el.createChild(
35524             {cls:'x-tree-headers'},this.innerCt.dom);
35525    
35526         var cols = this.columns, c;
35527         var totalWidth = 0;
35528         this.headEls = [];
35529         var  len = cols.length;
35530         for(var i = 0; i < len; i++){
35531              c = cols[i];
35532              totalWidth += c.width;
35533             this.headEls.push(this.headers.createChild({
35534                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35535                  cn: {
35536                      cls:'x-tree-hd-text',
35537                      html: c.header
35538                  },
35539                  style:'width:'+(c.width-this.borderWidth)+'px;'
35540              }));
35541         }
35542         this.headers.createChild({cls:'x-clear'});
35543         // prevent floats from wrapping when clipped
35544         this.headers.setWidth(totalWidth);
35545         //this.innerCt.setWidth(totalWidth);
35546         this.innerCt.setStyle({ overflow: 'auto' });
35547         this.onResize(this.width, this.height);
35548              
35549         
35550     },
35551     onResize : function(w,h)
35552     {
35553         this.height = h;
35554         this.width = w;
35555         // resize cols..
35556         this.innerCt.setWidth(this.width);
35557         this.innerCt.setHeight(this.height-20);
35558         
35559         // headers...
35560         var cols = this.columns, c;
35561         var totalWidth = 0;
35562         var expEl = false;
35563         var len = cols.length;
35564         for(var i = 0; i < len; i++){
35565             c = cols[i];
35566             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35567                 // it's the expander..
35568                 expEl  = this.headEls[i];
35569                 continue;
35570             }
35571             totalWidth += c.width;
35572             
35573         }
35574         if (expEl) {
35575             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35576         }
35577         this.headers.setWidth(w-20);
35578
35579         
35580         
35581         
35582     }
35583 });
35584 /*
35585  * Based on:
35586  * Ext JS Library 1.1.1
35587  * Copyright(c) 2006-2007, Ext JS, LLC.
35588  *
35589  * Originally Released Under LGPL - original licence link has changed is not relivant.
35590  *
35591  * Fork - LGPL
35592  * <script type="text/javascript">
35593  */
35594  
35595 /**
35596  * @class Roo.menu.Menu
35597  * @extends Roo.util.Observable
35598  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35599  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35600  * @constructor
35601  * Creates a new Menu
35602  * @param {Object} config Configuration options
35603  */
35604 Roo.menu.Menu = function(config){
35605     Roo.apply(this, config);
35606     this.id = this.id || Roo.id();
35607     this.addEvents({
35608         /**
35609          * @event beforeshow
35610          * Fires before this menu is displayed
35611          * @param {Roo.menu.Menu} this
35612          */
35613         beforeshow : true,
35614         /**
35615          * @event beforehide
35616          * Fires before this menu is hidden
35617          * @param {Roo.menu.Menu} this
35618          */
35619         beforehide : true,
35620         /**
35621          * @event show
35622          * Fires after this menu is displayed
35623          * @param {Roo.menu.Menu} this
35624          */
35625         show : true,
35626         /**
35627          * @event hide
35628          * Fires after this menu is hidden
35629          * @param {Roo.menu.Menu} this
35630          */
35631         hide : true,
35632         /**
35633          * @event click
35634          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35635          * @param {Roo.menu.Menu} this
35636          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35637          * @param {Roo.EventObject} e
35638          */
35639         click : true,
35640         /**
35641          * @event mouseover
35642          * Fires when the mouse is hovering over this menu
35643          * @param {Roo.menu.Menu} this
35644          * @param {Roo.EventObject} e
35645          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35646          */
35647         mouseover : true,
35648         /**
35649          * @event mouseout
35650          * Fires when the mouse exits this menu
35651          * @param {Roo.menu.Menu} this
35652          * @param {Roo.EventObject} e
35653          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35654          */
35655         mouseout : true,
35656         /**
35657          * @event itemclick
35658          * Fires when a menu item contained in this menu is clicked
35659          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35660          * @param {Roo.EventObject} e
35661          */
35662         itemclick: true
35663     });
35664     if (this.registerMenu) {
35665         Roo.menu.MenuMgr.register(this);
35666     }
35667     
35668     var mis = this.items;
35669     this.items = new Roo.util.MixedCollection();
35670     if(mis){
35671         this.add.apply(this, mis);
35672     }
35673 };
35674
35675 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35676     /**
35677      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35678      */
35679     minWidth : 120,
35680     /**
35681      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35682      * for bottom-right shadow (defaults to "sides")
35683      */
35684     shadow : "sides",
35685     /**
35686      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35687      * this menu (defaults to "tl-tr?")
35688      */
35689     subMenuAlign : "tl-tr?",
35690     /**
35691      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35692      * relative to its element of origin (defaults to "tl-bl?")
35693      */
35694     defaultAlign : "tl-bl?",
35695     /**
35696      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35697      */
35698     allowOtherMenus : false,
35699     /**
35700      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35701      */
35702     registerMenu : true,
35703
35704     hidden:true,
35705
35706     // private
35707     render : function(){
35708         if(this.el){
35709             return;
35710         }
35711         var el = this.el = new Roo.Layer({
35712             cls: "x-menu",
35713             shadow:this.shadow,
35714             constrain: false,
35715             parentEl: this.parentEl || document.body,
35716             zindex:15000
35717         });
35718
35719         this.keyNav = new Roo.menu.MenuNav(this);
35720
35721         if(this.plain){
35722             el.addClass("x-menu-plain");
35723         }
35724         if(this.cls){
35725             el.addClass(this.cls);
35726         }
35727         // generic focus element
35728         this.focusEl = el.createChild({
35729             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35730         });
35731         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35732         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35733         
35734         ul.on("mouseover", this.onMouseOver, this);
35735         ul.on("mouseout", this.onMouseOut, this);
35736         this.items.each(function(item){
35737             if (item.hidden) {
35738                 return;
35739             }
35740             
35741             var li = document.createElement("li");
35742             li.className = "x-menu-list-item";
35743             ul.dom.appendChild(li);
35744             item.render(li, this);
35745         }, this);
35746         this.ul = ul;
35747         this.autoWidth();
35748     },
35749
35750     // private
35751     autoWidth : function(){
35752         var el = this.el, ul = this.ul;
35753         if(!el){
35754             return;
35755         }
35756         var w = this.width;
35757         if(w){
35758             el.setWidth(w);
35759         }else if(Roo.isIE){
35760             el.setWidth(this.minWidth);
35761             var t = el.dom.offsetWidth; // force recalc
35762             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35763         }
35764     },
35765
35766     // private
35767     delayAutoWidth : function(){
35768         if(this.rendered){
35769             if(!this.awTask){
35770                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35771             }
35772             this.awTask.delay(20);
35773         }
35774     },
35775
35776     // private
35777     findTargetItem : function(e){
35778         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35779         if(t && t.menuItemId){
35780             return this.items.get(t.menuItemId);
35781         }
35782     },
35783
35784     // private
35785     onClick : function(e){
35786         Roo.log("menu.onClick");
35787         var t = this.findTargetItem(e);
35788         if(!t){
35789             return;
35790         }
35791         Roo.log(e);
35792         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35793             if(t == this.activeItem && t.shouldDeactivate(e)){
35794                 this.activeItem.deactivate();
35795                 delete this.activeItem;
35796                 return;
35797             }
35798             if(t.canActivate){
35799                 this.setActiveItem(t, true);
35800             }
35801             return;
35802             
35803             
35804         }
35805         
35806         t.onClick(e);
35807         this.fireEvent("click", this, t, e);
35808     },
35809
35810     // private
35811     setActiveItem : function(item, autoExpand){
35812         if(item != this.activeItem){
35813             if(this.activeItem){
35814                 this.activeItem.deactivate();
35815             }
35816             this.activeItem = item;
35817             item.activate(autoExpand);
35818         }else if(autoExpand){
35819             item.expandMenu();
35820         }
35821     },
35822
35823     // private
35824     tryActivate : function(start, step){
35825         var items = this.items;
35826         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35827             var item = items.get(i);
35828             if(!item.disabled && item.canActivate){
35829                 this.setActiveItem(item, false);
35830                 return item;
35831             }
35832         }
35833         return false;
35834     },
35835
35836     // private
35837     onMouseOver : function(e){
35838         var t;
35839         if(t = this.findTargetItem(e)){
35840             if(t.canActivate && !t.disabled){
35841                 this.setActiveItem(t, true);
35842             }
35843         }
35844         this.fireEvent("mouseover", this, e, t);
35845     },
35846
35847     // private
35848     onMouseOut : function(e){
35849         var t;
35850         if(t = this.findTargetItem(e)){
35851             if(t == this.activeItem && t.shouldDeactivate(e)){
35852                 this.activeItem.deactivate();
35853                 delete this.activeItem;
35854             }
35855         }
35856         this.fireEvent("mouseout", this, e, t);
35857     },
35858
35859     /**
35860      * Read-only.  Returns true if the menu is currently displayed, else false.
35861      * @type Boolean
35862      */
35863     isVisible : function(){
35864         return this.el && !this.hidden;
35865     },
35866
35867     /**
35868      * Displays this menu relative to another element
35869      * @param {String/HTMLElement/Roo.Element} element The element to align to
35870      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35871      * the element (defaults to this.defaultAlign)
35872      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35873      */
35874     show : function(el, pos, parentMenu){
35875         this.parentMenu = parentMenu;
35876         if(!this.el){
35877             this.render();
35878         }
35879         this.fireEvent("beforeshow", this);
35880         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35881     },
35882
35883     /**
35884      * Displays this menu at a specific xy position
35885      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35886      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35887      */
35888     showAt : function(xy, parentMenu, /* private: */_e){
35889         this.parentMenu = parentMenu;
35890         if(!this.el){
35891             this.render();
35892         }
35893         if(_e !== false){
35894             this.fireEvent("beforeshow", this);
35895             xy = this.el.adjustForConstraints(xy);
35896         }
35897         this.el.setXY(xy);
35898         this.el.show();
35899         this.hidden = false;
35900         this.focus();
35901         this.fireEvent("show", this);
35902     },
35903
35904     focus : function(){
35905         if(!this.hidden){
35906             this.doFocus.defer(50, this);
35907         }
35908     },
35909
35910     doFocus : function(){
35911         if(!this.hidden){
35912             this.focusEl.focus();
35913         }
35914     },
35915
35916     /**
35917      * Hides this menu and optionally all parent menus
35918      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35919      */
35920     hide : function(deep){
35921         if(this.el && this.isVisible()){
35922             this.fireEvent("beforehide", this);
35923             if(this.activeItem){
35924                 this.activeItem.deactivate();
35925                 this.activeItem = null;
35926             }
35927             this.el.hide();
35928             this.hidden = true;
35929             this.fireEvent("hide", this);
35930         }
35931         if(deep === true && this.parentMenu){
35932             this.parentMenu.hide(true);
35933         }
35934     },
35935
35936     /**
35937      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35938      * Any of the following are valid:
35939      * <ul>
35940      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35941      * <li>An HTMLElement object which will be converted to a menu item</li>
35942      * <li>A menu item config object that will be created as a new menu item</li>
35943      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35944      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35945      * </ul>
35946      * Usage:
35947      * <pre><code>
35948 // Create the menu
35949 var menu = new Roo.menu.Menu();
35950
35951 // Create a menu item to add by reference
35952 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35953
35954 // Add a bunch of items at once using different methods.
35955 // Only the last item added will be returned.
35956 var item = menu.add(
35957     menuItem,                // add existing item by ref
35958     'Dynamic Item',          // new TextItem
35959     '-',                     // new separator
35960     { text: 'Config Item' }  // new item by config
35961 );
35962 </code></pre>
35963      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35964      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35965      */
35966     add : function(){
35967         var a = arguments, l = a.length, item;
35968         for(var i = 0; i < l; i++){
35969             var el = a[i];
35970             if ((typeof(el) == "object") && el.xtype && el.xns) {
35971                 el = Roo.factory(el, Roo.menu);
35972             }
35973             
35974             if(el.render){ // some kind of Item
35975                 item = this.addItem(el);
35976             }else if(typeof el == "string"){ // string
35977                 if(el == "separator" || el == "-"){
35978                     item = this.addSeparator();
35979                 }else{
35980                     item = this.addText(el);
35981                 }
35982             }else if(el.tagName || el.el){ // element
35983                 item = this.addElement(el);
35984             }else if(typeof el == "object"){ // must be menu item config?
35985                 item = this.addMenuItem(el);
35986             }
35987         }
35988         return item;
35989     },
35990
35991     /**
35992      * Returns this menu's underlying {@link Roo.Element} object
35993      * @return {Roo.Element} The element
35994      */
35995     getEl : function(){
35996         if(!this.el){
35997             this.render();
35998         }
35999         return this.el;
36000     },
36001
36002     /**
36003      * Adds a separator bar to the menu
36004      * @return {Roo.menu.Item} The menu item that was added
36005      */
36006     addSeparator : function(){
36007         return this.addItem(new Roo.menu.Separator());
36008     },
36009
36010     /**
36011      * Adds an {@link Roo.Element} object to the menu
36012      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
36013      * @return {Roo.menu.Item} The menu item that was added
36014      */
36015     addElement : function(el){
36016         return this.addItem(new Roo.menu.BaseItem(el));
36017     },
36018
36019     /**
36020      * Adds an existing object based on {@link Roo.menu.Item} to the menu
36021      * @param {Roo.menu.Item} item The menu item to add
36022      * @return {Roo.menu.Item} The menu item that was added
36023      */
36024     addItem : function(item){
36025         this.items.add(item);
36026         if(this.ul){
36027             var li = document.createElement("li");
36028             li.className = "x-menu-list-item";
36029             this.ul.dom.appendChild(li);
36030             item.render(li, this);
36031             this.delayAutoWidth();
36032         }
36033         return item;
36034     },
36035
36036     /**
36037      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36038      * @param {Object} config A MenuItem config object
36039      * @return {Roo.menu.Item} The menu item that was added
36040      */
36041     addMenuItem : function(config){
36042         if(!(config instanceof Roo.menu.Item)){
36043             if(typeof config.checked == "boolean"){ // must be check menu item config?
36044                 config = new Roo.menu.CheckItem(config);
36045             }else{
36046                 config = new Roo.menu.Item(config);
36047             }
36048         }
36049         return this.addItem(config);
36050     },
36051
36052     /**
36053      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36054      * @param {String} text The text to display in the menu item
36055      * @return {Roo.menu.Item} The menu item that was added
36056      */
36057     addText : function(text){
36058         return this.addItem(new Roo.menu.TextItem({ text : text }));
36059     },
36060
36061     /**
36062      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36063      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36064      * @param {Roo.menu.Item} item The menu item to add
36065      * @return {Roo.menu.Item} The menu item that was added
36066      */
36067     insert : function(index, item){
36068         this.items.insert(index, item);
36069         if(this.ul){
36070             var li = document.createElement("li");
36071             li.className = "x-menu-list-item";
36072             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36073             item.render(li, this);
36074             this.delayAutoWidth();
36075         }
36076         return item;
36077     },
36078
36079     /**
36080      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36081      * @param {Roo.menu.Item} item The menu item to remove
36082      */
36083     remove : function(item){
36084         this.items.removeKey(item.id);
36085         item.destroy();
36086     },
36087
36088     /**
36089      * Removes and destroys all items in the menu
36090      */
36091     removeAll : function(){
36092         var f;
36093         while(f = this.items.first()){
36094             this.remove(f);
36095         }
36096     }
36097 });
36098
36099 // MenuNav is a private utility class used internally by the Menu
36100 Roo.menu.MenuNav = function(menu){
36101     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36102     this.scope = this.menu = menu;
36103 };
36104
36105 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36106     doRelay : function(e, h){
36107         var k = e.getKey();
36108         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36109             this.menu.tryActivate(0, 1);
36110             return false;
36111         }
36112         return h.call(this.scope || this, e, this.menu);
36113     },
36114
36115     up : function(e, m){
36116         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36117             m.tryActivate(m.items.length-1, -1);
36118         }
36119     },
36120
36121     down : function(e, m){
36122         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36123             m.tryActivate(0, 1);
36124         }
36125     },
36126
36127     right : function(e, m){
36128         if(m.activeItem){
36129             m.activeItem.expandMenu(true);
36130         }
36131     },
36132
36133     left : function(e, m){
36134         m.hide();
36135         if(m.parentMenu && m.parentMenu.activeItem){
36136             m.parentMenu.activeItem.activate();
36137         }
36138     },
36139
36140     enter : function(e, m){
36141         if(m.activeItem){
36142             e.stopPropagation();
36143             m.activeItem.onClick(e);
36144             m.fireEvent("click", this, m.activeItem);
36145             return true;
36146         }
36147     }
36148 });/*
36149  * Based on:
36150  * Ext JS Library 1.1.1
36151  * Copyright(c) 2006-2007, Ext JS, LLC.
36152  *
36153  * Originally Released Under LGPL - original licence link has changed is not relivant.
36154  *
36155  * Fork - LGPL
36156  * <script type="text/javascript">
36157  */
36158  
36159 /**
36160  * @class Roo.menu.MenuMgr
36161  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36162  * @singleton
36163  */
36164 Roo.menu.MenuMgr = function(){
36165    var menus, active, groups = {}, attached = false, lastShow = new Date();
36166
36167    // private - called when first menu is created
36168    function init(){
36169        menus = {};
36170        active = new Roo.util.MixedCollection();
36171        Roo.get(document).addKeyListener(27, function(){
36172            if(active.length > 0){
36173                hideAll();
36174            }
36175        });
36176    }
36177
36178    // private
36179    function hideAll(){
36180        if(active && active.length > 0){
36181            var c = active.clone();
36182            c.each(function(m){
36183                m.hide();
36184            });
36185        }
36186    }
36187
36188    // private
36189    function onHide(m){
36190        active.remove(m);
36191        if(active.length < 1){
36192            Roo.get(document).un("mousedown", onMouseDown);
36193            attached = false;
36194        }
36195    }
36196
36197    // private
36198    function onShow(m){
36199        var last = active.last();
36200        lastShow = new Date();
36201        active.add(m);
36202        if(!attached){
36203            Roo.get(document).on("mousedown", onMouseDown);
36204            attached = true;
36205        }
36206        if(m.parentMenu){
36207           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36208           m.parentMenu.activeChild = m;
36209        }else if(last && last.isVisible()){
36210           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36211        }
36212    }
36213
36214    // private
36215    function onBeforeHide(m){
36216        if(m.activeChild){
36217            m.activeChild.hide();
36218        }
36219        if(m.autoHideTimer){
36220            clearTimeout(m.autoHideTimer);
36221            delete m.autoHideTimer;
36222        }
36223    }
36224
36225    // private
36226    function onBeforeShow(m){
36227        var pm = m.parentMenu;
36228        if(!pm && !m.allowOtherMenus){
36229            hideAll();
36230        }else if(pm && pm.activeChild && active != m){
36231            pm.activeChild.hide();
36232        }
36233    }
36234
36235    // private
36236    function onMouseDown(e){
36237        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36238            hideAll();
36239        }
36240    }
36241
36242    // private
36243    function onBeforeCheck(mi, state){
36244        if(state){
36245            var g = groups[mi.group];
36246            for(var i = 0, l = g.length; i < l; i++){
36247                if(g[i] != mi){
36248                    g[i].setChecked(false);
36249                }
36250            }
36251        }
36252    }
36253
36254    return {
36255
36256        /**
36257         * Hides all menus that are currently visible
36258         */
36259        hideAll : function(){
36260             hideAll();  
36261        },
36262
36263        // private
36264        register : function(menu){
36265            if(!menus){
36266                init();
36267            }
36268            menus[menu.id] = menu;
36269            menu.on("beforehide", onBeforeHide);
36270            menu.on("hide", onHide);
36271            menu.on("beforeshow", onBeforeShow);
36272            menu.on("show", onShow);
36273            var g = menu.group;
36274            if(g && menu.events["checkchange"]){
36275                if(!groups[g]){
36276                    groups[g] = [];
36277                }
36278                groups[g].push(menu);
36279                menu.on("checkchange", onCheck);
36280            }
36281        },
36282
36283         /**
36284          * Returns a {@link Roo.menu.Menu} object
36285          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36286          * be used to generate and return a new Menu instance.
36287          */
36288        get : function(menu){
36289            if(typeof menu == "string"){ // menu id
36290                return menus[menu];
36291            }else if(menu.events){  // menu instance
36292                return menu;
36293            }else if(typeof menu.length == 'number'){ // array of menu items?
36294                return new Roo.menu.Menu({items:menu});
36295            }else{ // otherwise, must be a config
36296                return new Roo.menu.Menu(menu);
36297            }
36298        },
36299
36300        // private
36301        unregister : function(menu){
36302            delete menus[menu.id];
36303            menu.un("beforehide", onBeforeHide);
36304            menu.un("hide", onHide);
36305            menu.un("beforeshow", onBeforeShow);
36306            menu.un("show", onShow);
36307            var g = menu.group;
36308            if(g && menu.events["checkchange"]){
36309                groups[g].remove(menu);
36310                menu.un("checkchange", onCheck);
36311            }
36312        },
36313
36314        // private
36315        registerCheckable : function(menuItem){
36316            var g = menuItem.group;
36317            if(g){
36318                if(!groups[g]){
36319                    groups[g] = [];
36320                }
36321                groups[g].push(menuItem);
36322                menuItem.on("beforecheckchange", onBeforeCheck);
36323            }
36324        },
36325
36326        // private
36327        unregisterCheckable : function(menuItem){
36328            var g = menuItem.group;
36329            if(g){
36330                groups[g].remove(menuItem);
36331                menuItem.un("beforecheckchange", onBeforeCheck);
36332            }
36333        }
36334    };
36335 }();/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345  
36346
36347 /**
36348  * @class Roo.menu.BaseItem
36349  * @extends Roo.Component
36350  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36351  * management and base configuration options shared by all menu components.
36352  * @constructor
36353  * Creates a new BaseItem
36354  * @param {Object} config Configuration options
36355  */
36356 Roo.menu.BaseItem = function(config){
36357     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36358
36359     this.addEvents({
36360         /**
36361          * @event click
36362          * Fires when this item is clicked
36363          * @param {Roo.menu.BaseItem} this
36364          * @param {Roo.EventObject} e
36365          */
36366         click: true,
36367         /**
36368          * @event activate
36369          * Fires when this item is activated
36370          * @param {Roo.menu.BaseItem} this
36371          */
36372         activate : true,
36373         /**
36374          * @event deactivate
36375          * Fires when this item is deactivated
36376          * @param {Roo.menu.BaseItem} this
36377          */
36378         deactivate : true
36379     });
36380
36381     if(this.handler){
36382         this.on("click", this.handler, this.scope, true);
36383     }
36384 };
36385
36386 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36387     /**
36388      * @cfg {Function} handler
36389      * A function that will handle the click event of this menu item (defaults to undefined)
36390      */
36391     /**
36392      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36393      */
36394     canActivate : false,
36395     
36396      /**
36397      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36398      */
36399     hidden: false,
36400     
36401     /**
36402      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36403      */
36404     activeClass : "x-menu-item-active",
36405     /**
36406      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36407      */
36408     hideOnClick : true,
36409     /**
36410      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36411      */
36412     hideDelay : 100,
36413
36414     // private
36415     ctype: "Roo.menu.BaseItem",
36416
36417     // private
36418     actionMode : "container",
36419
36420     // private
36421     render : function(container, parentMenu){
36422         this.parentMenu = parentMenu;
36423         Roo.menu.BaseItem.superclass.render.call(this, container);
36424         this.container.menuItemId = this.id;
36425     },
36426
36427     // private
36428     onRender : function(container, position){
36429         this.el = Roo.get(this.el);
36430         container.dom.appendChild(this.el.dom);
36431     },
36432
36433     // private
36434     onClick : function(e){
36435         if(!this.disabled && this.fireEvent("click", this, e) !== false
36436                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36437             this.handleClick(e);
36438         }else{
36439             e.stopEvent();
36440         }
36441     },
36442
36443     // private
36444     activate : function(){
36445         if(this.disabled){
36446             return false;
36447         }
36448         var li = this.container;
36449         li.addClass(this.activeClass);
36450         this.region = li.getRegion().adjust(2, 2, -2, -2);
36451         this.fireEvent("activate", this);
36452         return true;
36453     },
36454
36455     // private
36456     deactivate : function(){
36457         this.container.removeClass(this.activeClass);
36458         this.fireEvent("deactivate", this);
36459     },
36460
36461     // private
36462     shouldDeactivate : function(e){
36463         return !this.region || !this.region.contains(e.getPoint());
36464     },
36465
36466     // private
36467     handleClick : function(e){
36468         if(this.hideOnClick){
36469             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36470         }
36471     },
36472
36473     // private
36474     expandMenu : function(autoActivate){
36475         // do nothing
36476     },
36477
36478     // private
36479     hideMenu : function(){
36480         // do nothing
36481     }
36482 });/*
36483  * Based on:
36484  * Ext JS Library 1.1.1
36485  * Copyright(c) 2006-2007, Ext JS, LLC.
36486  *
36487  * Originally Released Under LGPL - original licence link has changed is not relivant.
36488  *
36489  * Fork - LGPL
36490  * <script type="text/javascript">
36491  */
36492  
36493 /**
36494  * @class Roo.menu.Adapter
36495  * @extends Roo.menu.BaseItem
36496  * 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.
36497  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36498  * @constructor
36499  * Creates a new Adapter
36500  * @param {Object} config Configuration options
36501  */
36502 Roo.menu.Adapter = function(component, config){
36503     Roo.menu.Adapter.superclass.constructor.call(this, config);
36504     this.component = component;
36505 };
36506 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36507     // private
36508     canActivate : true,
36509
36510     // private
36511     onRender : function(container, position){
36512         this.component.render(container);
36513         this.el = this.component.getEl();
36514     },
36515
36516     // private
36517     activate : function(){
36518         if(this.disabled){
36519             return false;
36520         }
36521         this.component.focus();
36522         this.fireEvent("activate", this);
36523         return true;
36524     },
36525
36526     // private
36527     deactivate : function(){
36528         this.fireEvent("deactivate", this);
36529     },
36530
36531     // private
36532     disable : function(){
36533         this.component.disable();
36534         Roo.menu.Adapter.superclass.disable.call(this);
36535     },
36536
36537     // private
36538     enable : function(){
36539         this.component.enable();
36540         Roo.menu.Adapter.superclass.enable.call(this);
36541     }
36542 });/*
36543  * Based on:
36544  * Ext JS Library 1.1.1
36545  * Copyright(c) 2006-2007, Ext JS, LLC.
36546  *
36547  * Originally Released Under LGPL - original licence link has changed is not relivant.
36548  *
36549  * Fork - LGPL
36550  * <script type="text/javascript">
36551  */
36552
36553 /**
36554  * @class Roo.menu.TextItem
36555  * @extends Roo.menu.BaseItem
36556  * Adds a static text string to a menu, usually used as either a heading or group separator.
36557  * Note: old style constructor with text is still supported.
36558  * 
36559  * @constructor
36560  * Creates a new TextItem
36561  * @param {Object} cfg Configuration
36562  */
36563 Roo.menu.TextItem = function(cfg){
36564     if (typeof(cfg) == 'string') {
36565         this.text = cfg;
36566     } else {
36567         Roo.apply(this,cfg);
36568     }
36569     
36570     Roo.menu.TextItem.superclass.constructor.call(this);
36571 };
36572
36573 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36574     /**
36575      * @cfg {Boolean} text Text to show on item.
36576      */
36577     text : '',
36578     
36579     /**
36580      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36581      */
36582     hideOnClick : false,
36583     /**
36584      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36585      */
36586     itemCls : "x-menu-text",
36587
36588     // private
36589     onRender : function(){
36590         var s = document.createElement("span");
36591         s.className = this.itemCls;
36592         s.innerHTML = this.text;
36593         this.el = s;
36594         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36595     }
36596 });/*
36597  * Based on:
36598  * Ext JS Library 1.1.1
36599  * Copyright(c) 2006-2007, Ext JS, LLC.
36600  *
36601  * Originally Released Under LGPL - original licence link has changed is not relivant.
36602  *
36603  * Fork - LGPL
36604  * <script type="text/javascript">
36605  */
36606
36607 /**
36608  * @class Roo.menu.Separator
36609  * @extends Roo.menu.BaseItem
36610  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36611  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36612  * @constructor
36613  * @param {Object} config Configuration options
36614  */
36615 Roo.menu.Separator = function(config){
36616     Roo.menu.Separator.superclass.constructor.call(this, config);
36617 };
36618
36619 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36620     /**
36621      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36622      */
36623     itemCls : "x-menu-sep",
36624     /**
36625      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36626      */
36627     hideOnClick : false,
36628
36629     // private
36630     onRender : function(li){
36631         var s = document.createElement("span");
36632         s.className = this.itemCls;
36633         s.innerHTML = "&#160;";
36634         this.el = s;
36635         li.addClass("x-menu-sep-li");
36636         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36637     }
36638 });/*
36639  * Based on:
36640  * Ext JS Library 1.1.1
36641  * Copyright(c) 2006-2007, Ext JS, LLC.
36642  *
36643  * Originally Released Under LGPL - original licence link has changed is not relivant.
36644  *
36645  * Fork - LGPL
36646  * <script type="text/javascript">
36647  */
36648 /**
36649  * @class Roo.menu.Item
36650  * @extends Roo.menu.BaseItem
36651  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36652  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36653  * activation and click handling.
36654  * @constructor
36655  * Creates a new Item
36656  * @param {Object} config Configuration options
36657  */
36658 Roo.menu.Item = function(config){
36659     Roo.menu.Item.superclass.constructor.call(this, config);
36660     if(this.menu){
36661         this.menu = Roo.menu.MenuMgr.get(this.menu);
36662     }
36663 };
36664 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36665     
36666     /**
36667      * @cfg {String} text
36668      * The text to show on the menu item.
36669      */
36670     text: '',
36671      /**
36672      * @cfg {String} HTML to render in menu
36673      * The text to show on the menu item (HTML version).
36674      */
36675     html: '',
36676     /**
36677      * @cfg {String} icon
36678      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36679      */
36680     icon: undefined,
36681     /**
36682      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36683      */
36684     itemCls : "x-menu-item",
36685     /**
36686      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36687      */
36688     canActivate : true,
36689     /**
36690      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36691      */
36692     showDelay: 200,
36693     // doc'd in BaseItem
36694     hideDelay: 200,
36695
36696     // private
36697     ctype: "Roo.menu.Item",
36698     
36699     // private
36700     onRender : function(container, position){
36701         var el = document.createElement("a");
36702         el.hideFocus = true;
36703         el.unselectable = "on";
36704         el.href = this.href || "#";
36705         if(this.hrefTarget){
36706             el.target = this.hrefTarget;
36707         }
36708         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36709         
36710         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36711         
36712         el.innerHTML = String.format(
36713                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36714                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36715         this.el = el;
36716         Roo.menu.Item.superclass.onRender.call(this, container, position);
36717     },
36718
36719     /**
36720      * Sets the text to display in this menu item
36721      * @param {String} text The text to display
36722      * @param {Boolean} isHTML true to indicate text is pure html.
36723      */
36724     setText : function(text, isHTML){
36725         if (isHTML) {
36726             this.html = text;
36727         } else {
36728             this.text = text;
36729             this.html = '';
36730         }
36731         if(this.rendered){
36732             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36733      
36734             this.el.update(String.format(
36735                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36736                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36737             this.parentMenu.autoWidth();
36738         }
36739     },
36740
36741     // private
36742     handleClick : function(e){
36743         if(!this.href){ // if no link defined, stop the event automatically
36744             e.stopEvent();
36745         }
36746         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36747     },
36748
36749     // private
36750     activate : function(autoExpand){
36751         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36752             this.focus();
36753             if(autoExpand){
36754                 this.expandMenu();
36755             }
36756         }
36757         return true;
36758     },
36759
36760     // private
36761     shouldDeactivate : function(e){
36762         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36763             if(this.menu && this.menu.isVisible()){
36764                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36765             }
36766             return true;
36767         }
36768         return false;
36769     },
36770
36771     // private
36772     deactivate : function(){
36773         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36774         this.hideMenu();
36775     },
36776
36777     // private
36778     expandMenu : function(autoActivate){
36779         if(!this.disabled && this.menu){
36780             clearTimeout(this.hideTimer);
36781             delete this.hideTimer;
36782             if(!this.menu.isVisible() && !this.showTimer){
36783                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36784             }else if (this.menu.isVisible() && autoActivate){
36785                 this.menu.tryActivate(0, 1);
36786             }
36787         }
36788     },
36789
36790     // private
36791     deferExpand : function(autoActivate){
36792         delete this.showTimer;
36793         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36794         if(autoActivate){
36795             this.menu.tryActivate(0, 1);
36796         }
36797     },
36798
36799     // private
36800     hideMenu : function(){
36801         clearTimeout(this.showTimer);
36802         delete this.showTimer;
36803         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36804             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36805         }
36806     },
36807
36808     // private
36809     deferHide : function(){
36810         delete this.hideTimer;
36811         this.menu.hide();
36812     }
36813 });/*
36814  * Based on:
36815  * Ext JS Library 1.1.1
36816  * Copyright(c) 2006-2007, Ext JS, LLC.
36817  *
36818  * Originally Released Under LGPL - original licence link has changed is not relivant.
36819  *
36820  * Fork - LGPL
36821  * <script type="text/javascript">
36822  */
36823  
36824 /**
36825  * @class Roo.menu.CheckItem
36826  * @extends Roo.menu.Item
36827  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36828  * @constructor
36829  * Creates a new CheckItem
36830  * @param {Object} config Configuration options
36831  */
36832 Roo.menu.CheckItem = function(config){
36833     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36834     this.addEvents({
36835         /**
36836          * @event beforecheckchange
36837          * Fires before the checked value is set, providing an opportunity to cancel if needed
36838          * @param {Roo.menu.CheckItem} this
36839          * @param {Boolean} checked The new checked value that will be set
36840          */
36841         "beforecheckchange" : true,
36842         /**
36843          * @event checkchange
36844          * Fires after the checked value has been set
36845          * @param {Roo.menu.CheckItem} this
36846          * @param {Boolean} checked The checked value that was set
36847          */
36848         "checkchange" : true
36849     });
36850     if(this.checkHandler){
36851         this.on('checkchange', this.checkHandler, this.scope);
36852     }
36853 };
36854 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36855     /**
36856      * @cfg {String} group
36857      * All check items with the same group name will automatically be grouped into a single-select
36858      * radio button group (defaults to '')
36859      */
36860     /**
36861      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36862      */
36863     itemCls : "x-menu-item x-menu-check-item",
36864     /**
36865      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36866      */
36867     groupClass : "x-menu-group-item",
36868
36869     /**
36870      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36871      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36872      * initialized with checked = true will be rendered as checked.
36873      */
36874     checked: false,
36875
36876     // private
36877     ctype: "Roo.menu.CheckItem",
36878
36879     // private
36880     onRender : function(c){
36881         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36882         if(this.group){
36883             this.el.addClass(this.groupClass);
36884         }
36885         Roo.menu.MenuMgr.registerCheckable(this);
36886         if(this.checked){
36887             this.checked = false;
36888             this.setChecked(true, true);
36889         }
36890     },
36891
36892     // private
36893     destroy : function(){
36894         if(this.rendered){
36895             Roo.menu.MenuMgr.unregisterCheckable(this);
36896         }
36897         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36898     },
36899
36900     /**
36901      * Set the checked state of this item
36902      * @param {Boolean} checked The new checked value
36903      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36904      */
36905     setChecked : function(state, suppressEvent){
36906         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36907             if(this.container){
36908                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36909             }
36910             this.checked = state;
36911             if(suppressEvent !== true){
36912                 this.fireEvent("checkchange", this, state);
36913             }
36914         }
36915     },
36916
36917     // private
36918     handleClick : function(e){
36919        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36920            this.setChecked(!this.checked);
36921        }
36922        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36923     }
36924 });/*
36925  * Based on:
36926  * Ext JS Library 1.1.1
36927  * Copyright(c) 2006-2007, Ext JS, LLC.
36928  *
36929  * Originally Released Under LGPL - original licence link has changed is not relivant.
36930  *
36931  * Fork - LGPL
36932  * <script type="text/javascript">
36933  */
36934  
36935 /**
36936  * @class Roo.menu.DateItem
36937  * @extends Roo.menu.Adapter
36938  * A menu item that wraps the {@link Roo.DatPicker} component.
36939  * @constructor
36940  * Creates a new DateItem
36941  * @param {Object} config Configuration options
36942  */
36943 Roo.menu.DateItem = function(config){
36944     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36945     /** The Roo.DatePicker object @type Roo.DatePicker */
36946     this.picker = this.component;
36947     this.addEvents({select: true});
36948     
36949     this.picker.on("render", function(picker){
36950         picker.getEl().swallowEvent("click");
36951         picker.container.addClass("x-menu-date-item");
36952     });
36953
36954     this.picker.on("select", this.onSelect, this);
36955 };
36956
36957 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36958     // private
36959     onSelect : function(picker, date){
36960         this.fireEvent("select", this, date, picker);
36961         Roo.menu.DateItem.superclass.handleClick.call(this);
36962     }
36963 });/*
36964  * Based on:
36965  * Ext JS Library 1.1.1
36966  * Copyright(c) 2006-2007, Ext JS, LLC.
36967  *
36968  * Originally Released Under LGPL - original licence link has changed is not relivant.
36969  *
36970  * Fork - LGPL
36971  * <script type="text/javascript">
36972  */
36973  
36974 /**
36975  * @class Roo.menu.ColorItem
36976  * @extends Roo.menu.Adapter
36977  * A menu item that wraps the {@link Roo.ColorPalette} component.
36978  * @constructor
36979  * Creates a new ColorItem
36980  * @param {Object} config Configuration options
36981  */
36982 Roo.menu.ColorItem = function(config){
36983     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36984     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36985     this.palette = this.component;
36986     this.relayEvents(this.palette, ["select"]);
36987     if(this.selectHandler){
36988         this.on('select', this.selectHandler, this.scope);
36989     }
36990 };
36991 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36992  * Based on:
36993  * Ext JS Library 1.1.1
36994  * Copyright(c) 2006-2007, Ext JS, LLC.
36995  *
36996  * Originally Released Under LGPL - original licence link has changed is not relivant.
36997  *
36998  * Fork - LGPL
36999  * <script type="text/javascript">
37000  */
37001  
37002
37003 /**
37004  * @class Roo.menu.DateMenu
37005  * @extends Roo.menu.Menu
37006  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
37007  * @constructor
37008  * Creates a new DateMenu
37009  * @param {Object} config Configuration options
37010  */
37011 Roo.menu.DateMenu = function(config){
37012     Roo.menu.DateMenu.superclass.constructor.call(this, config);
37013     this.plain = true;
37014     var di = new Roo.menu.DateItem(config);
37015     this.add(di);
37016     /**
37017      * The {@link Roo.DatePicker} instance for this DateMenu
37018      * @type DatePicker
37019      */
37020     this.picker = di.picker;
37021     /**
37022      * @event select
37023      * @param {DatePicker} picker
37024      * @param {Date} date
37025      */
37026     this.relayEvents(di, ["select"]);
37027     this.on('beforeshow', function(){
37028         if(this.picker){
37029             this.picker.hideMonthPicker(false);
37030         }
37031     }, this);
37032 };
37033 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37034     cls:'x-date-menu'
37035 });/*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045  
37046
37047 /**
37048  * @class Roo.menu.ColorMenu
37049  * @extends Roo.menu.Menu
37050  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37051  * @constructor
37052  * Creates a new ColorMenu
37053  * @param {Object} config Configuration options
37054  */
37055 Roo.menu.ColorMenu = function(config){
37056     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37057     this.plain = true;
37058     var ci = new Roo.menu.ColorItem(config);
37059     this.add(ci);
37060     /**
37061      * The {@link Roo.ColorPalette} instance for this ColorMenu
37062      * @type ColorPalette
37063      */
37064     this.palette = ci.palette;
37065     /**
37066      * @event select
37067      * @param {ColorPalette} palette
37068      * @param {String} color
37069      */
37070     this.relayEvents(ci, ["select"]);
37071 };
37072 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37073  * Based on:
37074  * Ext JS Library 1.1.1
37075  * Copyright(c) 2006-2007, Ext JS, LLC.
37076  *
37077  * Originally Released Under LGPL - original licence link has changed is not relivant.
37078  *
37079  * Fork - LGPL
37080  * <script type="text/javascript">
37081  */
37082  
37083 /**
37084  * @class Roo.form.Field
37085  * @extends Roo.BoxComponent
37086  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37087  * @constructor
37088  * Creates a new Field
37089  * @param {Object} config Configuration options
37090  */
37091 Roo.form.Field = function(config){
37092     Roo.form.Field.superclass.constructor.call(this, config);
37093 };
37094
37095 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37096     /**
37097      * @cfg {String} fieldLabel Label to use when rendering a form.
37098      */
37099        /**
37100      * @cfg {String} qtip Mouse over tip
37101      */
37102      
37103     /**
37104      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37105      */
37106     invalidClass : "x-form-invalid",
37107     /**
37108      * @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")
37109      */
37110     invalidText : "The value in this field is invalid",
37111     /**
37112      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37113      */
37114     focusClass : "x-form-focus",
37115     /**
37116      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37117       automatic validation (defaults to "keyup").
37118      */
37119     validationEvent : "keyup",
37120     /**
37121      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37122      */
37123     validateOnBlur : true,
37124     /**
37125      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37126      */
37127     validationDelay : 250,
37128     /**
37129      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37130      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37131      */
37132     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37133     /**
37134      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37135      */
37136     fieldClass : "x-form-field",
37137     /**
37138      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37139      *<pre>
37140 Value         Description
37141 -----------   ----------------------------------------------------------------------
37142 qtip          Display a quick tip when the user hovers over the field
37143 title         Display a default browser title attribute popup
37144 under         Add a block div beneath the field containing the error text
37145 side          Add an error icon to the right of the field with a popup on hover
37146 [element id]  Add the error text directly to the innerHTML of the specified element
37147 </pre>
37148      */
37149     msgTarget : 'qtip',
37150     /**
37151      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37152      */
37153     msgFx : 'normal',
37154
37155     /**
37156      * @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.
37157      */
37158     readOnly : false,
37159
37160     /**
37161      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37162      */
37163     disabled : false,
37164
37165     /**
37166      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37167      */
37168     inputType : undefined,
37169     
37170     /**
37171      * @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).
37172          */
37173         tabIndex : undefined,
37174         
37175     // private
37176     isFormField : true,
37177
37178     // private
37179     hasFocus : false,
37180     /**
37181      * @property {Roo.Element} fieldEl
37182      * Element Containing the rendered Field (with label etc.)
37183      */
37184     /**
37185      * @cfg {Mixed} value A value to initialize this field with.
37186      */
37187     value : undefined,
37188
37189     /**
37190      * @cfg {String} name The field's HTML name attribute.
37191      */
37192     /**
37193      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37194      */
37195
37196         // private ??
37197         initComponent : function(){
37198         Roo.form.Field.superclass.initComponent.call(this);
37199         this.addEvents({
37200             /**
37201              * @event focus
37202              * Fires when this field receives input focus.
37203              * @param {Roo.form.Field} this
37204              */
37205             focus : true,
37206             /**
37207              * @event blur
37208              * Fires when this field loses input focus.
37209              * @param {Roo.form.Field} this
37210              */
37211             blur : true,
37212             /**
37213              * @event specialkey
37214              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37215              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37216              * @param {Roo.form.Field} this
37217              * @param {Roo.EventObject} e The event object
37218              */
37219             specialkey : true,
37220             /**
37221              * @event change
37222              * Fires just before the field blurs if the field value has changed.
37223              * @param {Roo.form.Field} this
37224              * @param {Mixed} newValue The new value
37225              * @param {Mixed} oldValue The original value
37226              */
37227             change : true,
37228             /**
37229              * @event invalid
37230              * Fires after the field has been marked as invalid.
37231              * @param {Roo.form.Field} this
37232              * @param {String} msg The validation message
37233              */
37234             invalid : true,
37235             /**
37236              * @event valid
37237              * Fires after the field has been validated with no errors.
37238              * @param {Roo.form.Field} this
37239              */
37240             valid : true,
37241              /**
37242              * @event keyup
37243              * Fires after the key up
37244              * @param {Roo.form.Field} this
37245              * @param {Roo.EventObject}  e The event Object
37246              */
37247             keyup : true
37248         });
37249     },
37250
37251     /**
37252      * Returns the name attribute of the field if available
37253      * @return {String} name The field name
37254      */
37255     getName: function(){
37256          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37257     },
37258
37259     // private
37260     onRender : function(ct, position){
37261         Roo.form.Field.superclass.onRender.call(this, ct, position);
37262         if(!this.el){
37263             var cfg = this.getAutoCreate();
37264             if(!cfg.name){
37265                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37266             }
37267             if (!cfg.name.length) {
37268                 delete cfg.name;
37269             }
37270             if(this.inputType){
37271                 cfg.type = this.inputType;
37272             }
37273             this.el = ct.createChild(cfg, position);
37274         }
37275         var type = this.el.dom.type;
37276         if(type){
37277             if(type == 'password'){
37278                 type = 'text';
37279             }
37280             this.el.addClass('x-form-'+type);
37281         }
37282         if(this.readOnly){
37283             this.el.dom.readOnly = true;
37284         }
37285         if(this.tabIndex !== undefined){
37286             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37287         }
37288
37289         this.el.addClass([this.fieldClass, this.cls]);
37290         this.initValue();
37291     },
37292
37293     /**
37294      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37295      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37296      * @return {Roo.form.Field} this
37297      */
37298     applyTo : function(target){
37299         this.allowDomMove = false;
37300         this.el = Roo.get(target);
37301         this.render(this.el.dom.parentNode);
37302         return this;
37303     },
37304
37305     // private
37306     initValue : function(){
37307         if(this.value !== undefined){
37308             this.setValue(this.value);
37309         }else if(this.el.dom.value.length > 0){
37310             this.setValue(this.el.dom.value);
37311         }
37312     },
37313
37314     /**
37315      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37316      */
37317     isDirty : function() {
37318         if(this.disabled) {
37319             return false;
37320         }
37321         return String(this.getValue()) !== String(this.originalValue);
37322     },
37323
37324     // private
37325     afterRender : function(){
37326         Roo.form.Field.superclass.afterRender.call(this);
37327         this.initEvents();
37328     },
37329
37330     // private
37331     fireKey : function(e){
37332         //Roo.log('field ' + e.getKey());
37333         if(e.isNavKeyPress()){
37334             this.fireEvent("specialkey", this, e);
37335         }
37336     },
37337
37338     /**
37339      * Resets the current field value to the originally loaded value and clears any validation messages
37340      */
37341     reset : function(){
37342         this.setValue(this.resetValue);
37343         this.clearInvalid();
37344     },
37345
37346     // private
37347     initEvents : function(){
37348         // safari killled keypress - so keydown is now used..
37349         this.el.on("keydown" , this.fireKey,  this);
37350         this.el.on("focus", this.onFocus,  this);
37351         this.el.on("blur", this.onBlur,  this);
37352         this.el.relayEvent('keyup', this);
37353
37354         // reference to original value for reset
37355         this.originalValue = this.getValue();
37356         this.resetValue =  this.getValue();
37357     },
37358
37359     // private
37360     onFocus : function(){
37361         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37362             this.el.addClass(this.focusClass);
37363         }
37364         if(!this.hasFocus){
37365             this.hasFocus = true;
37366             this.startValue = this.getValue();
37367             this.fireEvent("focus", this);
37368         }
37369     },
37370
37371     beforeBlur : Roo.emptyFn,
37372
37373     // private
37374     onBlur : function(){
37375         this.beforeBlur();
37376         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37377             this.el.removeClass(this.focusClass);
37378         }
37379         this.hasFocus = false;
37380         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37381             this.validate();
37382         }
37383         var v = this.getValue();
37384         if(String(v) !== String(this.startValue)){
37385             this.fireEvent('change', this, v, this.startValue);
37386         }
37387         this.fireEvent("blur", this);
37388     },
37389
37390     /**
37391      * Returns whether or not the field value is currently valid
37392      * @param {Boolean} preventMark True to disable marking the field invalid
37393      * @return {Boolean} True if the value is valid, else false
37394      */
37395     isValid : function(preventMark){
37396         if(this.disabled){
37397             return true;
37398         }
37399         var restore = this.preventMark;
37400         this.preventMark = preventMark === true;
37401         var v = this.validateValue(this.processValue(this.getRawValue()));
37402         this.preventMark = restore;
37403         return v;
37404     },
37405
37406     /**
37407      * Validates the field value
37408      * @return {Boolean} True if the value is valid, else false
37409      */
37410     validate : function(){
37411         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37412             this.clearInvalid();
37413             return true;
37414         }
37415         return false;
37416     },
37417
37418     processValue : function(value){
37419         return value;
37420     },
37421
37422     // private
37423     // Subclasses should provide the validation implementation by overriding this
37424     validateValue : function(value){
37425         return true;
37426     },
37427
37428     /**
37429      * Mark this field as invalid
37430      * @param {String} msg The validation message
37431      */
37432     markInvalid : function(msg){
37433         if(!this.rendered || this.preventMark){ // not rendered
37434             return;
37435         }
37436         
37437         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37438         
37439         obj.el.addClass(this.invalidClass);
37440         msg = msg || this.invalidText;
37441         switch(this.msgTarget){
37442             case 'qtip':
37443                 obj.el.dom.qtip = msg;
37444                 obj.el.dom.qclass = 'x-form-invalid-tip';
37445                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37446                     Roo.QuickTips.enable();
37447                 }
37448                 break;
37449             case 'title':
37450                 this.el.dom.title = msg;
37451                 break;
37452             case 'under':
37453                 if(!this.errorEl){
37454                     var elp = this.el.findParent('.x-form-element', 5, true);
37455                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37456                     this.errorEl.setWidth(elp.getWidth(true)-20);
37457                 }
37458                 this.errorEl.update(msg);
37459                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37460                 break;
37461             case 'side':
37462                 if(!this.errorIcon){
37463                     var elp = this.el.findParent('.x-form-element', 5, true);
37464                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37465                 }
37466                 this.alignErrorIcon();
37467                 this.errorIcon.dom.qtip = msg;
37468                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37469                 this.errorIcon.show();
37470                 this.on('resize', this.alignErrorIcon, this);
37471                 break;
37472             default:
37473                 var t = Roo.getDom(this.msgTarget);
37474                 t.innerHTML = msg;
37475                 t.style.display = this.msgDisplay;
37476                 break;
37477         }
37478         this.fireEvent('invalid', this, msg);
37479     },
37480
37481     // private
37482     alignErrorIcon : function(){
37483         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37484     },
37485
37486     /**
37487      * Clear any invalid styles/messages for this field
37488      */
37489     clearInvalid : function(){
37490         if(!this.rendered || this.preventMark){ // not rendered
37491             return;
37492         }
37493         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37494         
37495         obj.el.removeClass(this.invalidClass);
37496         switch(this.msgTarget){
37497             case 'qtip':
37498                 obj.el.dom.qtip = '';
37499                 break;
37500             case 'title':
37501                 this.el.dom.title = '';
37502                 break;
37503             case 'under':
37504                 if(this.errorEl){
37505                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37506                 }
37507                 break;
37508             case 'side':
37509                 if(this.errorIcon){
37510                     this.errorIcon.dom.qtip = '';
37511                     this.errorIcon.hide();
37512                     this.un('resize', this.alignErrorIcon, this);
37513                 }
37514                 break;
37515             default:
37516                 var t = Roo.getDom(this.msgTarget);
37517                 t.innerHTML = '';
37518                 t.style.display = 'none';
37519                 break;
37520         }
37521         this.fireEvent('valid', this);
37522     },
37523
37524     /**
37525      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37526      * @return {Mixed} value The field value
37527      */
37528     getRawValue : function(){
37529         var v = this.el.getValue();
37530         
37531         return v;
37532     },
37533
37534     /**
37535      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37536      * @return {Mixed} value The field value
37537      */
37538     getValue : function(){
37539         var v = this.el.getValue();
37540          
37541         return v;
37542     },
37543
37544     /**
37545      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37546      * @param {Mixed} value The value to set
37547      */
37548     setRawValue : function(v){
37549         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37550     },
37551
37552     /**
37553      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37554      * @param {Mixed} value The value to set
37555      */
37556     setValue : function(v){
37557         this.value = v;
37558         if(this.rendered){
37559             this.el.dom.value = (v === null || v === undefined ? '' : v);
37560              this.validate();
37561         }
37562     },
37563
37564     adjustSize : function(w, h){
37565         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37566         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37567         return s;
37568     },
37569
37570     adjustWidth : function(tag, w){
37571         tag = tag.toLowerCase();
37572         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37573             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37574                 if(tag == 'input'){
37575                     return w + 2;
37576                 }
37577                 if(tag == 'textarea'){
37578                     return w-2;
37579                 }
37580             }else if(Roo.isOpera){
37581                 if(tag == 'input'){
37582                     return w + 2;
37583                 }
37584                 if(tag == 'textarea'){
37585                     return w-2;
37586                 }
37587             }
37588         }
37589         return w;
37590     }
37591 });
37592
37593
37594 // anything other than normal should be considered experimental
37595 Roo.form.Field.msgFx = {
37596     normal : {
37597         show: function(msgEl, f){
37598             msgEl.setDisplayed('block');
37599         },
37600
37601         hide : function(msgEl, f){
37602             msgEl.setDisplayed(false).update('');
37603         }
37604     },
37605
37606     slide : {
37607         show: function(msgEl, f){
37608             msgEl.slideIn('t', {stopFx:true});
37609         },
37610
37611         hide : function(msgEl, f){
37612             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37613         }
37614     },
37615
37616     slideRight : {
37617         show: function(msgEl, f){
37618             msgEl.fixDisplay();
37619             msgEl.alignTo(f.el, 'tl-tr');
37620             msgEl.slideIn('l', {stopFx:true});
37621         },
37622
37623         hide : function(msgEl, f){
37624             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37625         }
37626     }
37627 };/*
37628  * Based on:
37629  * Ext JS Library 1.1.1
37630  * Copyright(c) 2006-2007, Ext JS, LLC.
37631  *
37632  * Originally Released Under LGPL - original licence link has changed is not relivant.
37633  *
37634  * Fork - LGPL
37635  * <script type="text/javascript">
37636  */
37637  
37638
37639 /**
37640  * @class Roo.form.TextField
37641  * @extends Roo.form.Field
37642  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37643  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37644  * @constructor
37645  * Creates a new TextField
37646  * @param {Object} config Configuration options
37647  */
37648 Roo.form.TextField = function(config){
37649     Roo.form.TextField.superclass.constructor.call(this, config);
37650     this.addEvents({
37651         /**
37652          * @event autosize
37653          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37654          * according to the default logic, but this event provides a hook for the developer to apply additional
37655          * logic at runtime to resize the field if needed.
37656              * @param {Roo.form.Field} this This text field
37657              * @param {Number} width The new field width
37658              */
37659         autosize : true
37660     });
37661 };
37662
37663 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37664     /**
37665      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37666      */
37667     grow : false,
37668     /**
37669      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37670      */
37671     growMin : 30,
37672     /**
37673      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37674      */
37675     growMax : 800,
37676     /**
37677      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37678      */
37679     vtype : null,
37680     /**
37681      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37682      */
37683     maskRe : null,
37684     /**
37685      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37686      */
37687     disableKeyFilter : false,
37688     /**
37689      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37690      */
37691     allowBlank : true,
37692     /**
37693      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37694      */
37695     minLength : 0,
37696     /**
37697      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37698      */
37699     maxLength : Number.MAX_VALUE,
37700     /**
37701      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37702      */
37703     minLengthText : "The minimum length for this field is {0}",
37704     /**
37705      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37706      */
37707     maxLengthText : "The maximum length for this field is {0}",
37708     /**
37709      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37710      */
37711     selectOnFocus : false,
37712     /**
37713      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37714      */
37715     blankText : "This field is required",
37716     /**
37717      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37718      * If available, this function will be called only after the basic validators all return true, and will be passed the
37719      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37720      */
37721     validator : null,
37722     /**
37723      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37724      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37725      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37726      */
37727     regex : null,
37728     /**
37729      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37730      */
37731     regexText : "",
37732     /**
37733      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37734      */
37735     emptyText : null,
37736    
37737
37738     // private
37739     initEvents : function()
37740     {
37741         if (this.emptyText) {
37742             this.el.attr('placeholder', this.emptyText);
37743         }
37744         
37745         Roo.form.TextField.superclass.initEvents.call(this);
37746         if(this.validationEvent == 'keyup'){
37747             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37748             this.el.on('keyup', this.filterValidation, this);
37749         }
37750         else if(this.validationEvent !== false){
37751             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37752         }
37753         
37754         if(this.selectOnFocus){
37755             this.on("focus", this.preFocus, this);
37756             
37757         }
37758         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37759             this.el.on("keypress", this.filterKeys, this);
37760         }
37761         if(this.grow){
37762             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37763             this.el.on("click", this.autoSize,  this);
37764         }
37765         if(this.el.is('input[type=password]') && Roo.isSafari){
37766             this.el.on('keydown', this.SafariOnKeyDown, this);
37767         }
37768     },
37769
37770     processValue : function(value){
37771         if(this.stripCharsRe){
37772             var newValue = value.replace(this.stripCharsRe, '');
37773             if(newValue !== value){
37774                 this.setRawValue(newValue);
37775                 return newValue;
37776             }
37777         }
37778         return value;
37779     },
37780
37781     filterValidation : function(e){
37782         if(!e.isNavKeyPress()){
37783             this.validationTask.delay(this.validationDelay);
37784         }
37785     },
37786
37787     // private
37788     onKeyUp : function(e){
37789         if(!e.isNavKeyPress()){
37790             this.autoSize();
37791         }
37792     },
37793
37794     /**
37795      * Resets the current field value to the originally-loaded value and clears any validation messages.
37796      *  
37797      */
37798     reset : function(){
37799         Roo.form.TextField.superclass.reset.call(this);
37800        
37801     },
37802
37803     
37804     // private
37805     preFocus : function(){
37806         
37807         if(this.selectOnFocus){
37808             this.el.dom.select();
37809         }
37810     },
37811
37812     
37813     // private
37814     filterKeys : function(e){
37815         var k = e.getKey();
37816         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37817             return;
37818         }
37819         var c = e.getCharCode(), cc = String.fromCharCode(c);
37820         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37821             return;
37822         }
37823         if(!this.maskRe.test(cc)){
37824             e.stopEvent();
37825         }
37826     },
37827
37828     setValue : function(v){
37829         
37830         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37831         
37832         this.autoSize();
37833     },
37834
37835     /**
37836      * Validates a value according to the field's validation rules and marks the field as invalid
37837      * if the validation fails
37838      * @param {Mixed} value The value to validate
37839      * @return {Boolean} True if the value is valid, else false
37840      */
37841     validateValue : function(value){
37842         if(value.length < 1)  { // if it's blank
37843              if(this.allowBlank){
37844                 this.clearInvalid();
37845                 return true;
37846              }else{
37847                 this.markInvalid(this.blankText);
37848                 return false;
37849              }
37850         }
37851         if(value.length < this.minLength){
37852             this.markInvalid(String.format(this.minLengthText, this.minLength));
37853             return false;
37854         }
37855         if(value.length > this.maxLength){
37856             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37857             return false;
37858         }
37859         if(this.vtype){
37860             var vt = Roo.form.VTypes;
37861             if(!vt[this.vtype](value, this)){
37862                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37863                 return false;
37864             }
37865         }
37866         if(typeof this.validator == "function"){
37867             var msg = this.validator(value);
37868             if(msg !== true){
37869                 this.markInvalid(msg);
37870                 return false;
37871             }
37872         }
37873         if(this.regex && !this.regex.test(value)){
37874             this.markInvalid(this.regexText);
37875             return false;
37876         }
37877         return true;
37878     },
37879
37880     /**
37881      * Selects text in this field
37882      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37883      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37884      */
37885     selectText : function(start, end){
37886         var v = this.getRawValue();
37887         if(v.length > 0){
37888             start = start === undefined ? 0 : start;
37889             end = end === undefined ? v.length : end;
37890             var d = this.el.dom;
37891             if(d.setSelectionRange){
37892                 d.setSelectionRange(start, end);
37893             }else if(d.createTextRange){
37894                 var range = d.createTextRange();
37895                 range.moveStart("character", start);
37896                 range.moveEnd("character", v.length-end);
37897                 range.select();
37898             }
37899         }
37900     },
37901
37902     /**
37903      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37904      * This only takes effect if grow = true, and fires the autosize event.
37905      */
37906     autoSize : function(){
37907         if(!this.grow || !this.rendered){
37908             return;
37909         }
37910         if(!this.metrics){
37911             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37912         }
37913         var el = this.el;
37914         var v = el.dom.value;
37915         var d = document.createElement('div');
37916         d.appendChild(document.createTextNode(v));
37917         v = d.innerHTML;
37918         d = null;
37919         v += "&#160;";
37920         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37921         this.el.setWidth(w);
37922         this.fireEvent("autosize", this, w);
37923     },
37924     
37925     // private
37926     SafariOnKeyDown : function(event)
37927     {
37928         // this is a workaround for a password hang bug on chrome/ webkit.
37929         
37930         var isSelectAll = false;
37931         
37932         if(this.el.dom.selectionEnd > 0){
37933             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37934         }
37935         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37936             event.preventDefault();
37937             this.setValue('');
37938             return;
37939         }
37940         
37941         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37942             
37943             event.preventDefault();
37944             // this is very hacky as keydown always get's upper case.
37945             
37946             var cc = String.fromCharCode(event.getCharCode());
37947             
37948             
37949             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37950             
37951         }
37952         
37953         
37954     }
37955 });/*
37956  * Based on:
37957  * Ext JS Library 1.1.1
37958  * Copyright(c) 2006-2007, Ext JS, LLC.
37959  *
37960  * Originally Released Under LGPL - original licence link has changed is not relivant.
37961  *
37962  * Fork - LGPL
37963  * <script type="text/javascript">
37964  */
37965  
37966 /**
37967  * @class Roo.form.Hidden
37968  * @extends Roo.form.TextField
37969  * Simple Hidden element used on forms 
37970  * 
37971  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37972  * 
37973  * @constructor
37974  * Creates a new Hidden form element.
37975  * @param {Object} config Configuration options
37976  */
37977
37978
37979
37980 // easy hidden field...
37981 Roo.form.Hidden = function(config){
37982     Roo.form.Hidden.superclass.constructor.call(this, config);
37983 };
37984   
37985 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37986     fieldLabel:      '',
37987     inputType:      'hidden',
37988     width:          50,
37989     allowBlank:     true,
37990     labelSeparator: '',
37991     hidden:         true,
37992     itemCls :       'x-form-item-display-none'
37993
37994
37995 });
37996
37997
37998 /*
37999  * Based on:
38000  * Ext JS Library 1.1.1
38001  * Copyright(c) 2006-2007, Ext JS, LLC.
38002  *
38003  * Originally Released Under LGPL - original licence link has changed is not relivant.
38004  *
38005  * Fork - LGPL
38006  * <script type="text/javascript">
38007  */
38008  
38009 /**
38010  * @class Roo.form.TriggerField
38011  * @extends Roo.form.TextField
38012  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
38013  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
38014  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
38015  * for which you can provide a custom implementation.  For example:
38016  * <pre><code>
38017 var trigger = new Roo.form.TriggerField();
38018 trigger.onTriggerClick = myTriggerFn;
38019 trigger.applyTo('my-field');
38020 </code></pre>
38021  *
38022  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
38023  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
38024  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38025  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
38026  * @constructor
38027  * Create a new TriggerField.
38028  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
38029  * to the base TextField)
38030  */
38031 Roo.form.TriggerField = function(config){
38032     this.mimicing = false;
38033     Roo.form.TriggerField.superclass.constructor.call(this, config);
38034 };
38035
38036 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38037     /**
38038      * @cfg {String} triggerClass A CSS class to apply to the trigger
38039      */
38040     /**
38041      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38042      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38043      */
38044     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38045     /**
38046      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38047      */
38048     hideTrigger:false,
38049
38050     /** @cfg {Boolean} grow @hide */
38051     /** @cfg {Number} growMin @hide */
38052     /** @cfg {Number} growMax @hide */
38053
38054     /**
38055      * @hide 
38056      * @method
38057      */
38058     autoSize: Roo.emptyFn,
38059     // private
38060     monitorTab : true,
38061     // private
38062     deferHeight : true,
38063
38064     
38065     actionMode : 'wrap',
38066     // private
38067     onResize : function(w, h){
38068         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38069         if(typeof w == 'number'){
38070             var x = w - this.trigger.getWidth();
38071             this.el.setWidth(this.adjustWidth('input', x));
38072             this.trigger.setStyle('left', x+'px');
38073         }
38074     },
38075
38076     // private
38077     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38078
38079     // private
38080     getResizeEl : function(){
38081         return this.wrap;
38082     },
38083
38084     // private
38085     getPositionEl : function(){
38086         return this.wrap;
38087     },
38088
38089     // private
38090     alignErrorIcon : function(){
38091         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38092     },
38093
38094     // private
38095     onRender : function(ct, position){
38096         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38097         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38098         this.trigger = this.wrap.createChild(this.triggerConfig ||
38099                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38100         if(this.hideTrigger){
38101             this.trigger.setDisplayed(false);
38102         }
38103         this.initTrigger();
38104         if(!this.width){
38105             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38106         }
38107     },
38108
38109     // private
38110     initTrigger : function(){
38111         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38112         this.trigger.addClassOnOver('x-form-trigger-over');
38113         this.trigger.addClassOnClick('x-form-trigger-click');
38114     },
38115
38116     // private
38117     onDestroy : function(){
38118         if(this.trigger){
38119             this.trigger.removeAllListeners();
38120             this.trigger.remove();
38121         }
38122         if(this.wrap){
38123             this.wrap.remove();
38124         }
38125         Roo.form.TriggerField.superclass.onDestroy.call(this);
38126     },
38127
38128     // private
38129     onFocus : function(){
38130         Roo.form.TriggerField.superclass.onFocus.call(this);
38131         if(!this.mimicing){
38132             this.wrap.addClass('x-trigger-wrap-focus');
38133             this.mimicing = true;
38134             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38135             if(this.monitorTab){
38136                 this.el.on("keydown", this.checkTab, this);
38137             }
38138         }
38139     },
38140
38141     // private
38142     checkTab : function(e){
38143         if(e.getKey() == e.TAB){
38144             this.triggerBlur();
38145         }
38146     },
38147
38148     // private
38149     onBlur : function(){
38150         // do nothing
38151     },
38152
38153     // private
38154     mimicBlur : function(e, t){
38155         if(!this.wrap.contains(t) && this.validateBlur()){
38156             this.triggerBlur();
38157         }
38158     },
38159
38160     // private
38161     triggerBlur : function(){
38162         this.mimicing = false;
38163         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38164         if(this.monitorTab){
38165             this.el.un("keydown", this.checkTab, this);
38166         }
38167         this.wrap.removeClass('x-trigger-wrap-focus');
38168         Roo.form.TriggerField.superclass.onBlur.call(this);
38169     },
38170
38171     // private
38172     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38173     validateBlur : function(e, t){
38174         return true;
38175     },
38176
38177     // private
38178     onDisable : function(){
38179         Roo.form.TriggerField.superclass.onDisable.call(this);
38180         if(this.wrap){
38181             this.wrap.addClass('x-item-disabled');
38182         }
38183     },
38184
38185     // private
38186     onEnable : function(){
38187         Roo.form.TriggerField.superclass.onEnable.call(this);
38188         if(this.wrap){
38189             this.wrap.removeClass('x-item-disabled');
38190         }
38191     },
38192
38193     // private
38194     onShow : function(){
38195         var ae = this.getActionEl();
38196         
38197         if(ae){
38198             ae.dom.style.display = '';
38199             ae.dom.style.visibility = 'visible';
38200         }
38201     },
38202
38203     // private
38204     
38205     onHide : function(){
38206         var ae = this.getActionEl();
38207         ae.dom.style.display = 'none';
38208     },
38209
38210     /**
38211      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38212      * by an implementing function.
38213      * @method
38214      * @param {EventObject} e
38215      */
38216     onTriggerClick : Roo.emptyFn
38217 });
38218
38219 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38220 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38221 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38222 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38223     initComponent : function(){
38224         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38225
38226         this.triggerConfig = {
38227             tag:'span', cls:'x-form-twin-triggers', cn:[
38228             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38229             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38230         ]};
38231     },
38232
38233     getTrigger : function(index){
38234         return this.triggers[index];
38235     },
38236
38237     initTrigger : function(){
38238         var ts = this.trigger.select('.x-form-trigger', true);
38239         this.wrap.setStyle('overflow', 'hidden');
38240         var triggerField = this;
38241         ts.each(function(t, all, index){
38242             t.hide = function(){
38243                 var w = triggerField.wrap.getWidth();
38244                 this.dom.style.display = 'none';
38245                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38246             };
38247             t.show = function(){
38248                 var w = triggerField.wrap.getWidth();
38249                 this.dom.style.display = '';
38250                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38251             };
38252             var triggerIndex = 'Trigger'+(index+1);
38253
38254             if(this['hide'+triggerIndex]){
38255                 t.dom.style.display = 'none';
38256             }
38257             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38258             t.addClassOnOver('x-form-trigger-over');
38259             t.addClassOnClick('x-form-trigger-click');
38260         }, this);
38261         this.triggers = ts.elements;
38262     },
38263
38264     onTrigger1Click : Roo.emptyFn,
38265     onTrigger2Click : Roo.emptyFn
38266 });/*
38267  * Based on:
38268  * Ext JS Library 1.1.1
38269  * Copyright(c) 2006-2007, Ext JS, LLC.
38270  *
38271  * Originally Released Under LGPL - original licence link has changed is not relivant.
38272  *
38273  * Fork - LGPL
38274  * <script type="text/javascript">
38275  */
38276  
38277 /**
38278  * @class Roo.form.TextArea
38279  * @extends Roo.form.TextField
38280  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38281  * support for auto-sizing.
38282  * @constructor
38283  * Creates a new TextArea
38284  * @param {Object} config Configuration options
38285  */
38286 Roo.form.TextArea = function(config){
38287     Roo.form.TextArea.superclass.constructor.call(this, config);
38288     // these are provided exchanges for backwards compat
38289     // minHeight/maxHeight were replaced by growMin/growMax to be
38290     // compatible with TextField growing config values
38291     if(this.minHeight !== undefined){
38292         this.growMin = this.minHeight;
38293     }
38294     if(this.maxHeight !== undefined){
38295         this.growMax = this.maxHeight;
38296     }
38297 };
38298
38299 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38300     /**
38301      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38302      */
38303     growMin : 60,
38304     /**
38305      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38306      */
38307     growMax: 1000,
38308     /**
38309      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38310      * in the field (equivalent to setting overflow: hidden, defaults to false)
38311      */
38312     preventScrollbars: false,
38313     /**
38314      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38315      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38316      */
38317
38318     // private
38319     onRender : function(ct, position){
38320         if(!this.el){
38321             this.defaultAutoCreate = {
38322                 tag: "textarea",
38323                 style:"width:300px;height:60px;",
38324                 autocomplete: "new-password"
38325             };
38326         }
38327         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38328         if(this.grow){
38329             this.textSizeEl = Roo.DomHelper.append(document.body, {
38330                 tag: "pre", cls: "x-form-grow-sizer"
38331             });
38332             if(this.preventScrollbars){
38333                 this.el.setStyle("overflow", "hidden");
38334             }
38335             this.el.setHeight(this.growMin);
38336         }
38337     },
38338
38339     onDestroy : function(){
38340         if(this.textSizeEl){
38341             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38342         }
38343         Roo.form.TextArea.superclass.onDestroy.call(this);
38344     },
38345
38346     // private
38347     onKeyUp : function(e){
38348         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38349             this.autoSize();
38350         }
38351     },
38352
38353     /**
38354      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38355      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38356      */
38357     autoSize : function(){
38358         if(!this.grow || !this.textSizeEl){
38359             return;
38360         }
38361         var el = this.el;
38362         var v = el.dom.value;
38363         var ts = this.textSizeEl;
38364
38365         ts.innerHTML = '';
38366         ts.appendChild(document.createTextNode(v));
38367         v = ts.innerHTML;
38368
38369         Roo.fly(ts).setWidth(this.el.getWidth());
38370         if(v.length < 1){
38371             v = "&#160;&#160;";
38372         }else{
38373             if(Roo.isIE){
38374                 v = v.replace(/\n/g, '<p>&#160;</p>');
38375             }
38376             v += "&#160;\n&#160;";
38377         }
38378         ts.innerHTML = v;
38379         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38380         if(h != this.lastHeight){
38381             this.lastHeight = h;
38382             this.el.setHeight(h);
38383             this.fireEvent("autosize", this, h);
38384         }
38385     }
38386 });/*
38387  * Based on:
38388  * Ext JS Library 1.1.1
38389  * Copyright(c) 2006-2007, Ext JS, LLC.
38390  *
38391  * Originally Released Under LGPL - original licence link has changed is not relivant.
38392  *
38393  * Fork - LGPL
38394  * <script type="text/javascript">
38395  */
38396  
38397
38398 /**
38399  * @class Roo.form.NumberField
38400  * @extends Roo.form.TextField
38401  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38402  * @constructor
38403  * Creates a new NumberField
38404  * @param {Object} config Configuration options
38405  */
38406 Roo.form.NumberField = function(config){
38407     Roo.form.NumberField.superclass.constructor.call(this, config);
38408 };
38409
38410 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38411     /**
38412      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38413      */
38414     fieldClass: "x-form-field x-form-num-field",
38415     /**
38416      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38417      */
38418     allowDecimals : true,
38419     /**
38420      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38421      */
38422     decimalSeparator : ".",
38423     /**
38424      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38425      */
38426     decimalPrecision : 2,
38427     /**
38428      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38429      */
38430     allowNegative : true,
38431     /**
38432      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38433      */
38434     minValue : Number.NEGATIVE_INFINITY,
38435     /**
38436      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38437      */
38438     maxValue : Number.MAX_VALUE,
38439     /**
38440      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38441      */
38442     minText : "The minimum value for this field is {0}",
38443     /**
38444      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38445      */
38446     maxText : "The maximum value for this field is {0}",
38447     /**
38448      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38449      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38450      */
38451     nanText : "{0} is not a valid number",
38452
38453     // private
38454     initEvents : function(){
38455         Roo.form.NumberField.superclass.initEvents.call(this);
38456         var allowed = "0123456789";
38457         if(this.allowDecimals){
38458             allowed += this.decimalSeparator;
38459         }
38460         if(this.allowNegative){
38461             allowed += "-";
38462         }
38463         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38464         var keyPress = function(e){
38465             var k = e.getKey();
38466             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38467                 return;
38468             }
38469             var c = e.getCharCode();
38470             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38471                 e.stopEvent();
38472             }
38473         };
38474         this.el.on("keypress", keyPress, this);
38475     },
38476
38477     // private
38478     validateValue : function(value){
38479         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38480             return false;
38481         }
38482         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38483              return true;
38484         }
38485         var num = this.parseValue(value);
38486         if(isNaN(num)){
38487             this.markInvalid(String.format(this.nanText, value));
38488             return false;
38489         }
38490         if(num < this.minValue){
38491             this.markInvalid(String.format(this.minText, this.minValue));
38492             return false;
38493         }
38494         if(num > this.maxValue){
38495             this.markInvalid(String.format(this.maxText, this.maxValue));
38496             return false;
38497         }
38498         return true;
38499     },
38500
38501     getValue : function(){
38502         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38503     },
38504
38505     // private
38506     parseValue : function(value){
38507         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38508         return isNaN(value) ? '' : value;
38509     },
38510
38511     // private
38512     fixPrecision : function(value){
38513         var nan = isNaN(value);
38514         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38515             return nan ? '' : value;
38516         }
38517         return parseFloat(value).toFixed(this.decimalPrecision);
38518     },
38519
38520     setValue : function(v){
38521         v = this.fixPrecision(v);
38522         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38523     },
38524
38525     // private
38526     decimalPrecisionFcn : function(v){
38527         return Math.floor(v);
38528     },
38529
38530     beforeBlur : function(){
38531         var v = this.parseValue(this.getRawValue());
38532         if(v){
38533             this.setValue(v);
38534         }
38535     }
38536 });/*
38537  * Based on:
38538  * Ext JS Library 1.1.1
38539  * Copyright(c) 2006-2007, Ext JS, LLC.
38540  *
38541  * Originally Released Under LGPL - original licence link has changed is not relivant.
38542  *
38543  * Fork - LGPL
38544  * <script type="text/javascript">
38545  */
38546  
38547 /**
38548  * @class Roo.form.DateField
38549  * @extends Roo.form.TriggerField
38550  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38551 * @constructor
38552 * Create a new DateField
38553 * @param {Object} config
38554  */
38555 Roo.form.DateField = function(config){
38556     Roo.form.DateField.superclass.constructor.call(this, config);
38557     
38558       this.addEvents({
38559          
38560         /**
38561          * @event select
38562          * Fires when a date is selected
38563              * @param {Roo.form.DateField} combo This combo box
38564              * @param {Date} date The date selected
38565              */
38566         'select' : true
38567          
38568     });
38569     
38570     
38571     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38572     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38573     this.ddMatch = null;
38574     if(this.disabledDates){
38575         var dd = this.disabledDates;
38576         var re = "(?:";
38577         for(var i = 0; i < dd.length; i++){
38578             re += dd[i];
38579             if(i != dd.length-1) re += "|";
38580         }
38581         this.ddMatch = new RegExp(re + ")");
38582     }
38583 };
38584
38585 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38586     /**
38587      * @cfg {String} format
38588      * The default date format string which can be overriden for localization support.  The format must be
38589      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38590      */
38591     format : "m/d/y",
38592     /**
38593      * @cfg {String} altFormats
38594      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38595      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38596      */
38597     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38598     /**
38599      * @cfg {Array} disabledDays
38600      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38601      */
38602     disabledDays : null,
38603     /**
38604      * @cfg {String} disabledDaysText
38605      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38606      */
38607     disabledDaysText : "Disabled",
38608     /**
38609      * @cfg {Array} disabledDates
38610      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38611      * expression so they are very powerful. Some examples:
38612      * <ul>
38613      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38614      * <li>["03/08", "09/16"] would disable those days for every year</li>
38615      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38616      * <li>["03/../2006"] would disable every day in March 2006</li>
38617      * <li>["^03"] would disable every day in every March</li>
38618      * </ul>
38619      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38620      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38621      */
38622     disabledDates : null,
38623     /**
38624      * @cfg {String} disabledDatesText
38625      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38626      */
38627     disabledDatesText : "Disabled",
38628     /**
38629      * @cfg {Date/String} minValue
38630      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38631      * valid format (defaults to null).
38632      */
38633     minValue : null,
38634     /**
38635      * @cfg {Date/String} maxValue
38636      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38637      * valid format (defaults to null).
38638      */
38639     maxValue : null,
38640     /**
38641      * @cfg {String} minText
38642      * The error text to display when the date in the cell is before minValue (defaults to
38643      * 'The date in this field must be after {minValue}').
38644      */
38645     minText : "The date in this field must be equal to or after {0}",
38646     /**
38647      * @cfg {String} maxText
38648      * The error text to display when the date in the cell is after maxValue (defaults to
38649      * 'The date in this field must be before {maxValue}').
38650      */
38651     maxText : "The date in this field must be equal to or before {0}",
38652     /**
38653      * @cfg {String} invalidText
38654      * The error text to display when the date in the field is invalid (defaults to
38655      * '{value} is not a valid date - it must be in the format {format}').
38656      */
38657     invalidText : "{0} is not a valid date - it must be in the format {1}",
38658     /**
38659      * @cfg {String} triggerClass
38660      * An additional CSS class used to style the trigger button.  The trigger will always get the
38661      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38662      * which displays a calendar icon).
38663      */
38664     triggerClass : 'x-form-date-trigger',
38665     
38666
38667     /**
38668      * @cfg {Boolean} useIso
38669      * if enabled, then the date field will use a hidden field to store the 
38670      * real value as iso formated date. default (false)
38671      */ 
38672     useIso : false,
38673     /**
38674      * @cfg {String/Object} autoCreate
38675      * A DomHelper element spec, or true for a default element spec (defaults to
38676      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38677      */ 
38678     // private
38679     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38680     
38681     // private
38682     hiddenField: false,
38683     
38684     onRender : function(ct, position)
38685     {
38686         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38687         if (this.useIso) {
38688             //this.el.dom.removeAttribute('name'); 
38689             Roo.log("Changing name?");
38690             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38691             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38692                     'before', true);
38693             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38694             // prevent input submission
38695             this.hiddenName = this.name;
38696         }
38697             
38698             
38699     },
38700     
38701     // private
38702     validateValue : function(value)
38703     {
38704         value = this.formatDate(value);
38705         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38706             Roo.log('super failed');
38707             return false;
38708         }
38709         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38710              return true;
38711         }
38712         var svalue = value;
38713         value = this.parseDate(value);
38714         if(!value){
38715             Roo.log('parse date failed' + svalue);
38716             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38717             return false;
38718         }
38719         var time = value.getTime();
38720         if(this.minValue && time < this.minValue.getTime()){
38721             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38722             return false;
38723         }
38724         if(this.maxValue && time > this.maxValue.getTime()){
38725             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38726             return false;
38727         }
38728         if(this.disabledDays){
38729             var day = value.getDay();
38730             for(var i = 0; i < this.disabledDays.length; i++) {
38731                 if(day === this.disabledDays[i]){
38732                     this.markInvalid(this.disabledDaysText);
38733                     return false;
38734                 }
38735             }
38736         }
38737         var fvalue = this.formatDate(value);
38738         if(this.ddMatch && this.ddMatch.test(fvalue)){
38739             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38740             return false;
38741         }
38742         return true;
38743     },
38744
38745     // private
38746     // Provides logic to override the default TriggerField.validateBlur which just returns true
38747     validateBlur : function(){
38748         return !this.menu || !this.menu.isVisible();
38749     },
38750     
38751     getName: function()
38752     {
38753         // returns hidden if it's set..
38754         if (!this.rendered) {return ''};
38755         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38756         
38757     },
38758
38759     /**
38760      * Returns the current date value of the date field.
38761      * @return {Date} The date value
38762      */
38763     getValue : function(){
38764         
38765         return  this.hiddenField ?
38766                 this.hiddenField.value :
38767                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38768     },
38769
38770     /**
38771      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38772      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38773      * (the default format used is "m/d/y").
38774      * <br />Usage:
38775      * <pre><code>
38776 //All of these calls set the same date value (May 4, 2006)
38777
38778 //Pass a date object:
38779 var dt = new Date('5/4/06');
38780 dateField.setValue(dt);
38781
38782 //Pass a date string (default format):
38783 dateField.setValue('5/4/06');
38784
38785 //Pass a date string (custom format):
38786 dateField.format = 'Y-m-d';
38787 dateField.setValue('2006-5-4');
38788 </code></pre>
38789      * @param {String/Date} date The date or valid date string
38790      */
38791     setValue : function(date){
38792         if (this.hiddenField) {
38793             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38794         }
38795         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38796         // make sure the value field is always stored as a date..
38797         this.value = this.parseDate(date);
38798         
38799         
38800     },
38801
38802     // private
38803     parseDate : function(value){
38804         if(!value || value instanceof Date){
38805             return value;
38806         }
38807         var v = Date.parseDate(value, this.format);
38808          if (!v && this.useIso) {
38809             v = Date.parseDate(value, 'Y-m-d');
38810         }
38811         if(!v && this.altFormats){
38812             if(!this.altFormatsArray){
38813                 this.altFormatsArray = this.altFormats.split("|");
38814             }
38815             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38816                 v = Date.parseDate(value, this.altFormatsArray[i]);
38817             }
38818         }
38819         return v;
38820     },
38821
38822     // private
38823     formatDate : function(date, fmt){
38824         return (!date || !(date instanceof Date)) ?
38825                date : date.dateFormat(fmt || this.format);
38826     },
38827
38828     // private
38829     menuListeners : {
38830         select: function(m, d){
38831             
38832             this.setValue(d);
38833             this.fireEvent('select', this, d);
38834         },
38835         show : function(){ // retain focus styling
38836             this.onFocus();
38837         },
38838         hide : function(){
38839             this.focus.defer(10, this);
38840             var ml = this.menuListeners;
38841             this.menu.un("select", ml.select,  this);
38842             this.menu.un("show", ml.show,  this);
38843             this.menu.un("hide", ml.hide,  this);
38844         }
38845     },
38846
38847     // private
38848     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38849     onTriggerClick : function(){
38850         if(this.disabled){
38851             return;
38852         }
38853         if(this.menu == null){
38854             this.menu = new Roo.menu.DateMenu();
38855         }
38856         Roo.apply(this.menu.picker,  {
38857             showClear: this.allowBlank,
38858             minDate : this.minValue,
38859             maxDate : this.maxValue,
38860             disabledDatesRE : this.ddMatch,
38861             disabledDatesText : this.disabledDatesText,
38862             disabledDays : this.disabledDays,
38863             disabledDaysText : this.disabledDaysText,
38864             format : this.useIso ? 'Y-m-d' : this.format,
38865             minText : String.format(this.minText, this.formatDate(this.minValue)),
38866             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38867         });
38868         this.menu.on(Roo.apply({}, this.menuListeners, {
38869             scope:this
38870         }));
38871         this.menu.picker.setValue(this.getValue() || new Date());
38872         this.menu.show(this.el, "tl-bl?");
38873     },
38874
38875     beforeBlur : function(){
38876         var v = this.parseDate(this.getRawValue());
38877         if(v){
38878             this.setValue(v);
38879         }
38880     },
38881
38882     /*@
38883      * overide
38884      * 
38885      */
38886     isDirty : function() {
38887         if(this.disabled) {
38888             return false;
38889         }
38890         
38891         if(typeof(this.startValue) === 'undefined'){
38892             return false;
38893         }
38894         
38895         return String(this.getValue()) !== String(this.startValue);
38896         
38897     }
38898 });/*
38899  * Based on:
38900  * Ext JS Library 1.1.1
38901  * Copyright(c) 2006-2007, Ext JS, LLC.
38902  *
38903  * Originally Released Under LGPL - original licence link has changed is not relivant.
38904  *
38905  * Fork - LGPL
38906  * <script type="text/javascript">
38907  */
38908  
38909 /**
38910  * @class Roo.form.MonthField
38911  * @extends Roo.form.TriggerField
38912  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38913 * @constructor
38914 * Create a new MonthField
38915 * @param {Object} config
38916  */
38917 Roo.form.MonthField = function(config){
38918     
38919     Roo.form.MonthField.superclass.constructor.call(this, config);
38920     
38921       this.addEvents({
38922          
38923         /**
38924          * @event select
38925          * Fires when a date is selected
38926              * @param {Roo.form.MonthFieeld} combo This combo box
38927              * @param {Date} date The date selected
38928              */
38929         'select' : true
38930          
38931     });
38932     
38933     
38934     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38935     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38936     this.ddMatch = null;
38937     if(this.disabledDates){
38938         var dd = this.disabledDates;
38939         var re = "(?:";
38940         for(var i = 0; i < dd.length; i++){
38941             re += dd[i];
38942             if(i != dd.length-1) re += "|";
38943         }
38944         this.ddMatch = new RegExp(re + ")");
38945     }
38946 };
38947
38948 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38949     /**
38950      * @cfg {String} format
38951      * The default date format string which can be overriden for localization support.  The format must be
38952      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38953      */
38954     format : "M Y",
38955     /**
38956      * @cfg {String} altFormats
38957      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38958      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38959      */
38960     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38961     /**
38962      * @cfg {Array} disabledDays
38963      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38964      */
38965     disabledDays : [0,1,2,3,4,5,6],
38966     /**
38967      * @cfg {String} disabledDaysText
38968      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38969      */
38970     disabledDaysText : "Disabled",
38971     /**
38972      * @cfg {Array} disabledDates
38973      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38974      * expression so they are very powerful. Some examples:
38975      * <ul>
38976      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38977      * <li>["03/08", "09/16"] would disable those days for every year</li>
38978      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38979      * <li>["03/../2006"] would disable every day in March 2006</li>
38980      * <li>["^03"] would disable every day in every March</li>
38981      * </ul>
38982      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38983      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38984      */
38985     disabledDates : null,
38986     /**
38987      * @cfg {String} disabledDatesText
38988      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38989      */
38990     disabledDatesText : "Disabled",
38991     /**
38992      * @cfg {Date/String} minValue
38993      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38994      * valid format (defaults to null).
38995      */
38996     minValue : null,
38997     /**
38998      * @cfg {Date/String} maxValue
38999      * The maximum allowed date. Can be either a Javascript date object or a string date in a
39000      * valid format (defaults to null).
39001      */
39002     maxValue : null,
39003     /**
39004      * @cfg {String} minText
39005      * The error text to display when the date in the cell is before minValue (defaults to
39006      * 'The date in this field must be after {minValue}').
39007      */
39008     minText : "The date in this field must be equal to or after {0}",
39009     /**
39010      * @cfg {String} maxTextf
39011      * The error text to display when the date in the cell is after maxValue (defaults to
39012      * 'The date in this field must be before {maxValue}').
39013      */
39014     maxText : "The date in this field must be equal to or before {0}",
39015     /**
39016      * @cfg {String} invalidText
39017      * The error text to display when the date in the field is invalid (defaults to
39018      * '{value} is not a valid date - it must be in the format {format}').
39019      */
39020     invalidText : "{0} is not a valid date - it must be in the format {1}",
39021     /**
39022      * @cfg {String} triggerClass
39023      * An additional CSS class used to style the trigger button.  The trigger will always get the
39024      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
39025      * which displays a calendar icon).
39026      */
39027     triggerClass : 'x-form-date-trigger',
39028     
39029
39030     /**
39031      * @cfg {Boolean} useIso
39032      * if enabled, then the date field will use a hidden field to store the 
39033      * real value as iso formated date. default (true)
39034      */ 
39035     useIso : true,
39036     /**
39037      * @cfg {String/Object} autoCreate
39038      * A DomHelper element spec, or true for a default element spec (defaults to
39039      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39040      */ 
39041     // private
39042     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39043     
39044     // private
39045     hiddenField: false,
39046     
39047     hideMonthPicker : false,
39048     
39049     onRender : function(ct, position)
39050     {
39051         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39052         if (this.useIso) {
39053             this.el.dom.removeAttribute('name'); 
39054             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39055                     'before', true);
39056             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39057             // prevent input submission
39058             this.hiddenName = this.name;
39059         }
39060             
39061             
39062     },
39063     
39064     // private
39065     validateValue : function(value)
39066     {
39067         value = this.formatDate(value);
39068         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39069             return false;
39070         }
39071         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39072              return true;
39073         }
39074         var svalue = value;
39075         value = this.parseDate(value);
39076         if(!value){
39077             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39078             return false;
39079         }
39080         var time = value.getTime();
39081         if(this.minValue && time < this.minValue.getTime()){
39082             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39083             return false;
39084         }
39085         if(this.maxValue && time > this.maxValue.getTime()){
39086             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39087             return false;
39088         }
39089         /*if(this.disabledDays){
39090             var day = value.getDay();
39091             for(var i = 0; i < this.disabledDays.length; i++) {
39092                 if(day === this.disabledDays[i]){
39093                     this.markInvalid(this.disabledDaysText);
39094                     return false;
39095                 }
39096             }
39097         }
39098         */
39099         var fvalue = this.formatDate(value);
39100         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39101             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39102             return false;
39103         }
39104         */
39105         return true;
39106     },
39107
39108     // private
39109     // Provides logic to override the default TriggerField.validateBlur which just returns true
39110     validateBlur : function(){
39111         return !this.menu || !this.menu.isVisible();
39112     },
39113
39114     /**
39115      * Returns the current date value of the date field.
39116      * @return {Date} The date value
39117      */
39118     getValue : function(){
39119         
39120         
39121         
39122         return  this.hiddenField ?
39123                 this.hiddenField.value :
39124                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39125     },
39126
39127     /**
39128      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39129      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39130      * (the default format used is "m/d/y").
39131      * <br />Usage:
39132      * <pre><code>
39133 //All of these calls set the same date value (May 4, 2006)
39134
39135 //Pass a date object:
39136 var dt = new Date('5/4/06');
39137 monthField.setValue(dt);
39138
39139 //Pass a date string (default format):
39140 monthField.setValue('5/4/06');
39141
39142 //Pass a date string (custom format):
39143 monthField.format = 'Y-m-d';
39144 monthField.setValue('2006-5-4');
39145 </code></pre>
39146      * @param {String/Date} date The date or valid date string
39147      */
39148     setValue : function(date){
39149         Roo.log('month setValue' + date);
39150         // can only be first of month..
39151         
39152         var val = this.parseDate(date);
39153         
39154         if (this.hiddenField) {
39155             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39156         }
39157         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39158         this.value = this.parseDate(date);
39159     },
39160
39161     // private
39162     parseDate : function(value){
39163         if(!value || value instanceof Date){
39164             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39165             return value;
39166         }
39167         var v = Date.parseDate(value, this.format);
39168         if (!v && this.useIso) {
39169             v = Date.parseDate(value, 'Y-m-d');
39170         }
39171         if (v) {
39172             // 
39173             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39174         }
39175         
39176         
39177         if(!v && this.altFormats){
39178             if(!this.altFormatsArray){
39179                 this.altFormatsArray = this.altFormats.split("|");
39180             }
39181             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39182                 v = Date.parseDate(value, this.altFormatsArray[i]);
39183             }
39184         }
39185         return v;
39186     },
39187
39188     // private
39189     formatDate : function(date, fmt){
39190         return (!date || !(date instanceof Date)) ?
39191                date : date.dateFormat(fmt || this.format);
39192     },
39193
39194     // private
39195     menuListeners : {
39196         select: function(m, d){
39197             this.setValue(d);
39198             this.fireEvent('select', this, d);
39199         },
39200         show : function(){ // retain focus styling
39201             this.onFocus();
39202         },
39203         hide : function(){
39204             this.focus.defer(10, this);
39205             var ml = this.menuListeners;
39206             this.menu.un("select", ml.select,  this);
39207             this.menu.un("show", ml.show,  this);
39208             this.menu.un("hide", ml.hide,  this);
39209         }
39210     },
39211     // private
39212     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39213     onTriggerClick : function(){
39214         if(this.disabled){
39215             return;
39216         }
39217         if(this.menu == null){
39218             this.menu = new Roo.menu.DateMenu();
39219            
39220         }
39221         
39222         Roo.apply(this.menu.picker,  {
39223             
39224             showClear: this.allowBlank,
39225             minDate : this.minValue,
39226             maxDate : this.maxValue,
39227             disabledDatesRE : this.ddMatch,
39228             disabledDatesText : this.disabledDatesText,
39229             
39230             format : this.useIso ? 'Y-m-d' : this.format,
39231             minText : String.format(this.minText, this.formatDate(this.minValue)),
39232             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39233             
39234         });
39235          this.menu.on(Roo.apply({}, this.menuListeners, {
39236             scope:this
39237         }));
39238        
39239         
39240         var m = this.menu;
39241         var p = m.picker;
39242         
39243         // hide month picker get's called when we called by 'before hide';
39244         
39245         var ignorehide = true;
39246         p.hideMonthPicker  = function(disableAnim){
39247             if (ignorehide) {
39248                 return;
39249             }
39250              if(this.monthPicker){
39251                 Roo.log("hideMonthPicker called");
39252                 if(disableAnim === true){
39253                     this.monthPicker.hide();
39254                 }else{
39255                     this.monthPicker.slideOut('t', {duration:.2});
39256                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39257                     p.fireEvent("select", this, this.value);
39258                     m.hide();
39259                 }
39260             }
39261         }
39262         
39263         Roo.log('picker set value');
39264         Roo.log(this.getValue());
39265         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39266         m.show(this.el, 'tl-bl?');
39267         ignorehide  = false;
39268         // this will trigger hideMonthPicker..
39269         
39270         
39271         // hidden the day picker
39272         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39273         
39274         
39275         
39276       
39277         
39278         p.showMonthPicker.defer(100, p);
39279     
39280         
39281        
39282     },
39283
39284     beforeBlur : function(){
39285         var v = this.parseDate(this.getRawValue());
39286         if(v){
39287             this.setValue(v);
39288         }
39289     }
39290
39291     /** @cfg {Boolean} grow @hide */
39292     /** @cfg {Number} growMin @hide */
39293     /** @cfg {Number} growMax @hide */
39294     /**
39295      * @hide
39296      * @method autoSize
39297      */
39298 });/*
39299  * Based on:
39300  * Ext JS Library 1.1.1
39301  * Copyright(c) 2006-2007, Ext JS, LLC.
39302  *
39303  * Originally Released Under LGPL - original licence link has changed is not relivant.
39304  *
39305  * Fork - LGPL
39306  * <script type="text/javascript">
39307  */
39308  
39309
39310 /**
39311  * @class Roo.form.ComboBox
39312  * @extends Roo.form.TriggerField
39313  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39314  * @constructor
39315  * Create a new ComboBox.
39316  * @param {Object} config Configuration options
39317  */
39318 Roo.form.ComboBox = function(config){
39319     Roo.form.ComboBox.superclass.constructor.call(this, config);
39320     this.addEvents({
39321         /**
39322          * @event expand
39323          * Fires when the dropdown list is expanded
39324              * @param {Roo.form.ComboBox} combo This combo box
39325              */
39326         'expand' : true,
39327         /**
39328          * @event collapse
39329          * Fires when the dropdown list is collapsed
39330              * @param {Roo.form.ComboBox} combo This combo box
39331              */
39332         'collapse' : true,
39333         /**
39334          * @event beforeselect
39335          * Fires before a list item is selected. Return false to cancel the selection.
39336              * @param {Roo.form.ComboBox} combo This combo box
39337              * @param {Roo.data.Record} record The data record returned from the underlying store
39338              * @param {Number} index The index of the selected item in the dropdown list
39339              */
39340         'beforeselect' : true,
39341         /**
39342          * @event select
39343          * Fires when a list item is selected
39344              * @param {Roo.form.ComboBox} combo This combo box
39345              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39346              * @param {Number} index The index of the selected item in the dropdown list
39347              */
39348         'select' : true,
39349         /**
39350          * @event beforequery
39351          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39352          * The event object passed has these properties:
39353              * @param {Roo.form.ComboBox} combo This combo box
39354              * @param {String} query The query
39355              * @param {Boolean} forceAll true to force "all" query
39356              * @param {Boolean} cancel true to cancel the query
39357              * @param {Object} e The query event object
39358              */
39359         'beforequery': true,
39360          /**
39361          * @event add
39362          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39363              * @param {Roo.form.ComboBox} combo This combo box
39364              */
39365         'add' : true,
39366         /**
39367          * @event edit
39368          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39369              * @param {Roo.form.ComboBox} combo This combo box
39370              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39371              */
39372         'edit' : true
39373         
39374         
39375     });
39376     if(this.transform){
39377         this.allowDomMove = false;
39378         var s = Roo.getDom(this.transform);
39379         if(!this.hiddenName){
39380             this.hiddenName = s.name;
39381         }
39382         if(!this.store){
39383             this.mode = 'local';
39384             var d = [], opts = s.options;
39385             for(var i = 0, len = opts.length;i < len; i++){
39386                 var o = opts[i];
39387                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39388                 if(o.selected) {
39389                     this.value = value;
39390                 }
39391                 d.push([value, o.text]);
39392             }
39393             this.store = new Roo.data.SimpleStore({
39394                 'id': 0,
39395                 fields: ['value', 'text'],
39396                 data : d
39397             });
39398             this.valueField = 'value';
39399             this.displayField = 'text';
39400         }
39401         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39402         if(!this.lazyRender){
39403             this.target = true;
39404             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39405             s.parentNode.removeChild(s); // remove it
39406             this.render(this.el.parentNode);
39407         }else{
39408             s.parentNode.removeChild(s); // remove it
39409         }
39410
39411     }
39412     if (this.store) {
39413         this.store = Roo.factory(this.store, Roo.data);
39414     }
39415     
39416     this.selectedIndex = -1;
39417     if(this.mode == 'local'){
39418         if(config.queryDelay === undefined){
39419             this.queryDelay = 10;
39420         }
39421         if(config.minChars === undefined){
39422             this.minChars = 0;
39423         }
39424     }
39425 };
39426
39427 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39428     /**
39429      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39430      */
39431     /**
39432      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39433      * rendering into an Roo.Editor, defaults to false)
39434      */
39435     /**
39436      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39437      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39438      */
39439     /**
39440      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39441      */
39442     /**
39443      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39444      * the dropdown list (defaults to undefined, with no header element)
39445      */
39446
39447      /**
39448      * @cfg {String/Roo.Template} tpl The template to use to render the output
39449      */
39450      
39451     // private
39452     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39453     /**
39454      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39455      */
39456     listWidth: undefined,
39457     /**
39458      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39459      * mode = 'remote' or 'text' if mode = 'local')
39460      */
39461     displayField: undefined,
39462     /**
39463      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39464      * mode = 'remote' or 'value' if mode = 'local'). 
39465      * Note: use of a valueField requires the user make a selection
39466      * in order for a value to be mapped.
39467      */
39468     valueField: undefined,
39469     
39470     
39471     /**
39472      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39473      * field's data value (defaults to the underlying DOM element's name)
39474      */
39475     hiddenName: undefined,
39476     /**
39477      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39478      */
39479     listClass: '',
39480     /**
39481      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39482      */
39483     selectedClass: 'x-combo-selected',
39484     /**
39485      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39486      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39487      * which displays a downward arrow icon).
39488      */
39489     triggerClass : 'x-form-arrow-trigger',
39490     /**
39491      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39492      */
39493     shadow:'sides',
39494     /**
39495      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39496      * anchor positions (defaults to 'tl-bl')
39497      */
39498     listAlign: 'tl-bl?',
39499     /**
39500      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39501      */
39502     maxHeight: 300,
39503     /**
39504      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39505      * query specified by the allQuery config option (defaults to 'query')
39506      */
39507     triggerAction: 'query',
39508     /**
39509      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39510      * (defaults to 4, does not apply if editable = false)
39511      */
39512     minChars : 4,
39513     /**
39514      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39515      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39516      */
39517     typeAhead: false,
39518     /**
39519      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39520      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39521      */
39522     queryDelay: 500,
39523     /**
39524      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39525      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39526      */
39527     pageSize: 0,
39528     /**
39529      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39530      * when editable = true (defaults to false)
39531      */
39532     selectOnFocus:false,
39533     /**
39534      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39535      */
39536     queryParam: 'query',
39537     /**
39538      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39539      * when mode = 'remote' (defaults to 'Loading...')
39540      */
39541     loadingText: 'Loading...',
39542     /**
39543      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39544      */
39545     resizable: false,
39546     /**
39547      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39548      */
39549     handleHeight : 8,
39550     /**
39551      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39552      * traditional select (defaults to true)
39553      */
39554     editable: true,
39555     /**
39556      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39557      */
39558     allQuery: '',
39559     /**
39560      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39561      */
39562     mode: 'remote',
39563     /**
39564      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39565      * listWidth has a higher value)
39566      */
39567     minListWidth : 70,
39568     /**
39569      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39570      * allow the user to set arbitrary text into the field (defaults to false)
39571      */
39572     forceSelection:false,
39573     /**
39574      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39575      * if typeAhead = true (defaults to 250)
39576      */
39577     typeAheadDelay : 250,
39578     /**
39579      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39580      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39581      */
39582     valueNotFoundText : undefined,
39583     /**
39584      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39585      */
39586     blockFocus : false,
39587     
39588     /**
39589      * @cfg {Boolean} disableClear Disable showing of clear button.
39590      */
39591     disableClear : false,
39592     /**
39593      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39594      */
39595     alwaysQuery : false,
39596     
39597     //private
39598     addicon : false,
39599     editicon: false,
39600     
39601     // element that contains real text value.. (when hidden is used..)
39602      
39603     // private
39604     onRender : function(ct, position){
39605         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39606         if(this.hiddenName){
39607             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39608                     'before', true);
39609             this.hiddenField.value =
39610                 this.hiddenValue !== undefined ? this.hiddenValue :
39611                 this.value !== undefined ? this.value : '';
39612
39613             // prevent input submission
39614             this.el.dom.removeAttribute('name');
39615              
39616              
39617         }
39618         if(Roo.isGecko){
39619             this.el.dom.setAttribute('autocomplete', 'off');
39620         }
39621
39622         var cls = 'x-combo-list';
39623
39624         this.list = new Roo.Layer({
39625             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39626         });
39627
39628         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39629         this.list.setWidth(lw);
39630         this.list.swallowEvent('mousewheel');
39631         this.assetHeight = 0;
39632
39633         if(this.title){
39634             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39635             this.assetHeight += this.header.getHeight();
39636         }
39637
39638         this.innerList = this.list.createChild({cls:cls+'-inner'});
39639         this.innerList.on('mouseover', this.onViewOver, this);
39640         this.innerList.on('mousemove', this.onViewMove, this);
39641         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39642         
39643         if(this.allowBlank && !this.pageSize && !this.disableClear){
39644             this.footer = this.list.createChild({cls:cls+'-ft'});
39645             this.pageTb = new Roo.Toolbar(this.footer);
39646            
39647         }
39648         if(this.pageSize){
39649             this.footer = this.list.createChild({cls:cls+'-ft'});
39650             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39651                     {pageSize: this.pageSize});
39652             
39653         }
39654         
39655         if (this.pageTb && this.allowBlank && !this.disableClear) {
39656             var _this = this;
39657             this.pageTb.add(new Roo.Toolbar.Fill(), {
39658                 cls: 'x-btn-icon x-btn-clear',
39659                 text: '&#160;',
39660                 handler: function()
39661                 {
39662                     _this.collapse();
39663                     _this.clearValue();
39664                     _this.onSelect(false, -1);
39665                 }
39666             });
39667         }
39668         if (this.footer) {
39669             this.assetHeight += this.footer.getHeight();
39670         }
39671         
39672
39673         if(!this.tpl){
39674             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39675         }
39676
39677         this.view = new Roo.View(this.innerList, this.tpl, {
39678             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39679         });
39680
39681         this.view.on('click', this.onViewClick, this);
39682
39683         this.store.on('beforeload', this.onBeforeLoad, this);
39684         this.store.on('load', this.onLoad, this);
39685         this.store.on('loadexception', this.onLoadException, this);
39686
39687         if(this.resizable){
39688             this.resizer = new Roo.Resizable(this.list,  {
39689                pinned:true, handles:'se'
39690             });
39691             this.resizer.on('resize', function(r, w, h){
39692                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39693                 this.listWidth = w;
39694                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39695                 this.restrictHeight();
39696             }, this);
39697             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39698         }
39699         if(!this.editable){
39700             this.editable = true;
39701             this.setEditable(false);
39702         }  
39703         
39704         
39705         if (typeof(this.events.add.listeners) != 'undefined') {
39706             
39707             this.addicon = this.wrap.createChild(
39708                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39709        
39710             this.addicon.on('click', function(e) {
39711                 this.fireEvent('add', this);
39712             }, this);
39713         }
39714         if (typeof(this.events.edit.listeners) != 'undefined') {
39715             
39716             this.editicon = this.wrap.createChild(
39717                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39718             if (this.addicon) {
39719                 this.editicon.setStyle('margin-left', '40px');
39720             }
39721             this.editicon.on('click', function(e) {
39722                 
39723                 // we fire even  if inothing is selected..
39724                 this.fireEvent('edit', this, this.lastData );
39725                 
39726             }, this);
39727         }
39728         
39729         
39730         
39731     },
39732
39733     // private
39734     initEvents : function(){
39735         Roo.form.ComboBox.superclass.initEvents.call(this);
39736
39737         this.keyNav = new Roo.KeyNav(this.el, {
39738             "up" : function(e){
39739                 this.inKeyMode = true;
39740                 this.selectPrev();
39741             },
39742
39743             "down" : function(e){
39744                 if(!this.isExpanded()){
39745                     this.onTriggerClick();
39746                 }else{
39747                     this.inKeyMode = true;
39748                     this.selectNext();
39749                 }
39750             },
39751
39752             "enter" : function(e){
39753                 this.onViewClick();
39754                 //return true;
39755             },
39756
39757             "esc" : function(e){
39758                 this.collapse();
39759             },
39760
39761             "tab" : function(e){
39762                 this.onViewClick(false);
39763                 this.fireEvent("specialkey", this, e);
39764                 return true;
39765             },
39766
39767             scope : this,
39768
39769             doRelay : function(foo, bar, hname){
39770                 if(hname == 'down' || this.scope.isExpanded()){
39771                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39772                 }
39773                 return true;
39774             },
39775
39776             forceKeyDown: true
39777         });
39778         this.queryDelay = Math.max(this.queryDelay || 10,
39779                 this.mode == 'local' ? 10 : 250);
39780         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39781         if(this.typeAhead){
39782             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39783         }
39784         if(this.editable !== false){
39785             this.el.on("keyup", this.onKeyUp, this);
39786         }
39787         if(this.forceSelection){
39788             this.on('blur', this.doForce, this);
39789         }
39790     },
39791
39792     onDestroy : function(){
39793         if(this.view){
39794             this.view.setStore(null);
39795             this.view.el.removeAllListeners();
39796             this.view.el.remove();
39797             this.view.purgeListeners();
39798         }
39799         if(this.list){
39800             this.list.destroy();
39801         }
39802         if(this.store){
39803             this.store.un('beforeload', this.onBeforeLoad, this);
39804             this.store.un('load', this.onLoad, this);
39805             this.store.un('loadexception', this.onLoadException, this);
39806         }
39807         Roo.form.ComboBox.superclass.onDestroy.call(this);
39808     },
39809
39810     // private
39811     fireKey : function(e){
39812         if(e.isNavKeyPress() && !this.list.isVisible()){
39813             this.fireEvent("specialkey", this, e);
39814         }
39815     },
39816
39817     // private
39818     onResize: function(w, h){
39819         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39820         
39821         if(typeof w != 'number'){
39822             // we do not handle it!?!?
39823             return;
39824         }
39825         var tw = this.trigger.getWidth();
39826         tw += this.addicon ? this.addicon.getWidth() : 0;
39827         tw += this.editicon ? this.editicon.getWidth() : 0;
39828         var x = w - tw;
39829         this.el.setWidth( this.adjustWidth('input', x));
39830             
39831         this.trigger.setStyle('left', x+'px');
39832         
39833         if(this.list && this.listWidth === undefined){
39834             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39835             this.list.setWidth(lw);
39836             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39837         }
39838         
39839     
39840         
39841     },
39842
39843     /**
39844      * Allow or prevent the user from directly editing the field text.  If false is passed,
39845      * the user will only be able to select from the items defined in the dropdown list.  This method
39846      * is the runtime equivalent of setting the 'editable' config option at config time.
39847      * @param {Boolean} value True to allow the user to directly edit the field text
39848      */
39849     setEditable : function(value){
39850         if(value == this.editable){
39851             return;
39852         }
39853         this.editable = value;
39854         if(!value){
39855             this.el.dom.setAttribute('readOnly', true);
39856             this.el.on('mousedown', this.onTriggerClick,  this);
39857             this.el.addClass('x-combo-noedit');
39858         }else{
39859             this.el.dom.setAttribute('readOnly', false);
39860             this.el.un('mousedown', this.onTriggerClick,  this);
39861             this.el.removeClass('x-combo-noedit');
39862         }
39863     },
39864
39865     // private
39866     onBeforeLoad : function(){
39867         if(!this.hasFocus){
39868             return;
39869         }
39870         this.innerList.update(this.loadingText ?
39871                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39872         this.restrictHeight();
39873         this.selectedIndex = -1;
39874     },
39875
39876     // private
39877     onLoad : function(){
39878         if(!this.hasFocus){
39879             return;
39880         }
39881         if(this.store.getCount() > 0){
39882             this.expand();
39883             this.restrictHeight();
39884             if(this.lastQuery == this.allQuery){
39885                 if(this.editable){
39886                     this.el.dom.select();
39887                 }
39888                 if(!this.selectByValue(this.value, true)){
39889                     this.select(0, true);
39890                 }
39891             }else{
39892                 this.selectNext();
39893                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39894                     this.taTask.delay(this.typeAheadDelay);
39895                 }
39896             }
39897         }else{
39898             this.onEmptyResults();
39899         }
39900         //this.el.focus();
39901     },
39902     // private
39903     onLoadException : function()
39904     {
39905         this.collapse();
39906         Roo.log(this.store.reader.jsonData);
39907         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39908             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39909         }
39910         
39911         
39912     },
39913     // private
39914     onTypeAhead : function(){
39915         if(this.store.getCount() > 0){
39916             var r = this.store.getAt(0);
39917             var newValue = r.data[this.displayField];
39918             var len = newValue.length;
39919             var selStart = this.getRawValue().length;
39920             if(selStart != len){
39921                 this.setRawValue(newValue);
39922                 this.selectText(selStart, newValue.length);
39923             }
39924         }
39925     },
39926
39927     // private
39928     onSelect : function(record, index){
39929         if(this.fireEvent('beforeselect', this, record, index) !== false){
39930             this.setFromData(index > -1 ? record.data : false);
39931             this.collapse();
39932             this.fireEvent('select', this, record, index);
39933         }
39934     },
39935
39936     /**
39937      * Returns the currently selected field value or empty string if no value is set.
39938      * @return {String} value The selected value
39939      */
39940     getValue : function(){
39941         if(this.valueField){
39942             return typeof this.value != 'undefined' ? this.value : '';
39943         }
39944         return Roo.form.ComboBox.superclass.getValue.call(this);
39945     },
39946
39947     /**
39948      * Clears any text/value currently set in the field
39949      */
39950     clearValue : function(){
39951         if(this.hiddenField){
39952             this.hiddenField.value = '';
39953         }
39954         this.value = '';
39955         this.setRawValue('');
39956         this.lastSelectionText = '';
39957         
39958     },
39959
39960     /**
39961      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39962      * will be displayed in the field.  If the value does not match the data value of an existing item,
39963      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39964      * Otherwise the field will be blank (although the value will still be set).
39965      * @param {String} value The value to match
39966      */
39967     setValue : function(v){
39968         var text = v;
39969         if(this.valueField){
39970             var r = this.findRecord(this.valueField, v);
39971             if(r){
39972                 text = r.data[this.displayField];
39973             }else if(this.valueNotFoundText !== undefined){
39974                 text = this.valueNotFoundText;
39975             }
39976         }
39977         this.lastSelectionText = text;
39978         if(this.hiddenField){
39979             this.hiddenField.value = v;
39980         }
39981         Roo.form.ComboBox.superclass.setValue.call(this, text);
39982         this.value = v;
39983     },
39984     /**
39985      * @property {Object} the last set data for the element
39986      */
39987     
39988     lastData : false,
39989     /**
39990      * Sets the value of the field based on a object which is related to the record format for the store.
39991      * @param {Object} value the value to set as. or false on reset?
39992      */
39993     setFromData : function(o){
39994         var dv = ''; // display value
39995         var vv = ''; // value value..
39996         this.lastData = o;
39997         if (this.displayField) {
39998             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39999         } else {
40000             // this is an error condition!!!
40001             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
40002         }
40003         
40004         if(this.valueField){
40005             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
40006         }
40007         if(this.hiddenField){
40008             this.hiddenField.value = vv;
40009             
40010             this.lastSelectionText = dv;
40011             Roo.form.ComboBox.superclass.setValue.call(this, dv);
40012             this.value = vv;
40013             return;
40014         }
40015         // no hidden field.. - we store the value in 'value', but still display
40016         // display field!!!!
40017         this.lastSelectionText = dv;
40018         Roo.form.ComboBox.superclass.setValue.call(this, dv);
40019         this.value = vv;
40020         
40021         
40022     },
40023     // private
40024     reset : function(){
40025         // overridden so that last data is reset..
40026         this.setValue(this.resetValue);
40027         this.clearInvalid();
40028         this.lastData = false;
40029         if (this.view) {
40030             this.view.clearSelections();
40031         }
40032     },
40033     // private
40034     findRecord : function(prop, value){
40035         var record;
40036         if(this.store.getCount() > 0){
40037             this.store.each(function(r){
40038                 if(r.data[prop] == value){
40039                     record = r;
40040                     return false;
40041                 }
40042                 return true;
40043             });
40044         }
40045         return record;
40046     },
40047     
40048     getName: function()
40049     {
40050         // returns hidden if it's set..
40051         if (!this.rendered) {return ''};
40052         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40053         
40054     },
40055     // private
40056     onViewMove : function(e, t){
40057         this.inKeyMode = false;
40058     },
40059
40060     // private
40061     onViewOver : function(e, t){
40062         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40063             return;
40064         }
40065         var item = this.view.findItemFromChild(t);
40066         if(item){
40067             var index = this.view.indexOf(item);
40068             this.select(index, false);
40069         }
40070     },
40071
40072     // private
40073     onViewClick : function(doFocus)
40074     {
40075         var index = this.view.getSelectedIndexes()[0];
40076         var r = this.store.getAt(index);
40077         if(r){
40078             this.onSelect(r, index);
40079         }
40080         if(doFocus !== false && !this.blockFocus){
40081             this.el.focus();
40082         }
40083     },
40084
40085     // private
40086     restrictHeight : function(){
40087         this.innerList.dom.style.height = '';
40088         var inner = this.innerList.dom;
40089         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40090         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40091         this.list.beginUpdate();
40092         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40093         this.list.alignTo(this.el, this.listAlign);
40094         this.list.endUpdate();
40095     },
40096
40097     // private
40098     onEmptyResults : function(){
40099         this.collapse();
40100     },
40101
40102     /**
40103      * Returns true if the dropdown list is expanded, else false.
40104      */
40105     isExpanded : function(){
40106         return this.list.isVisible();
40107     },
40108
40109     /**
40110      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40111      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40112      * @param {String} value The data value of the item to select
40113      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40114      * selected item if it is not currently in view (defaults to true)
40115      * @return {Boolean} True if the value matched an item in the list, else false
40116      */
40117     selectByValue : function(v, scrollIntoView){
40118         if(v !== undefined && v !== null){
40119             var r = this.findRecord(this.valueField || this.displayField, v);
40120             if(r){
40121                 this.select(this.store.indexOf(r), scrollIntoView);
40122                 return true;
40123             }
40124         }
40125         return false;
40126     },
40127
40128     /**
40129      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40130      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40131      * @param {Number} index The zero-based index of the list item to select
40132      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40133      * selected item if it is not currently in view (defaults to true)
40134      */
40135     select : function(index, scrollIntoView){
40136         this.selectedIndex = index;
40137         this.view.select(index);
40138         if(scrollIntoView !== false){
40139             var el = this.view.getNode(index);
40140             if(el){
40141                 this.innerList.scrollChildIntoView(el, false);
40142             }
40143         }
40144     },
40145
40146     // private
40147     selectNext : function(){
40148         var ct = this.store.getCount();
40149         if(ct > 0){
40150             if(this.selectedIndex == -1){
40151                 this.select(0);
40152             }else if(this.selectedIndex < ct-1){
40153                 this.select(this.selectedIndex+1);
40154             }
40155         }
40156     },
40157
40158     // private
40159     selectPrev : function(){
40160         var ct = this.store.getCount();
40161         if(ct > 0){
40162             if(this.selectedIndex == -1){
40163                 this.select(0);
40164             }else if(this.selectedIndex != 0){
40165                 this.select(this.selectedIndex-1);
40166             }
40167         }
40168     },
40169
40170     // private
40171     onKeyUp : function(e){
40172         if(this.editable !== false && !e.isSpecialKey()){
40173             this.lastKey = e.getKey();
40174             this.dqTask.delay(this.queryDelay);
40175         }
40176     },
40177
40178     // private
40179     validateBlur : function(){
40180         return !this.list || !this.list.isVisible();   
40181     },
40182
40183     // private
40184     initQuery : function(){
40185         this.doQuery(this.getRawValue());
40186     },
40187
40188     // private
40189     doForce : function(){
40190         if(this.el.dom.value.length > 0){
40191             this.el.dom.value =
40192                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40193              
40194         }
40195     },
40196
40197     /**
40198      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40199      * query allowing the query action to be canceled if needed.
40200      * @param {String} query The SQL query to execute
40201      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40202      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40203      * saved in the current store (defaults to false)
40204      */
40205     doQuery : function(q, forceAll){
40206         if(q === undefined || q === null){
40207             q = '';
40208         }
40209         var qe = {
40210             query: q,
40211             forceAll: forceAll,
40212             combo: this,
40213             cancel:false
40214         };
40215         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40216             return false;
40217         }
40218         q = qe.query;
40219         forceAll = qe.forceAll;
40220         if(forceAll === true || (q.length >= this.minChars)){
40221             if(this.lastQuery != q || this.alwaysQuery){
40222                 this.lastQuery = q;
40223                 if(this.mode == 'local'){
40224                     this.selectedIndex = -1;
40225                     if(forceAll){
40226                         this.store.clearFilter();
40227                     }else{
40228                         this.store.filter(this.displayField, q);
40229                     }
40230                     this.onLoad();
40231                 }else{
40232                     this.store.baseParams[this.queryParam] = q;
40233                     this.store.load({
40234                         params: this.getParams(q)
40235                     });
40236                     this.expand();
40237                 }
40238             }else{
40239                 this.selectedIndex = -1;
40240                 this.onLoad();   
40241             }
40242         }
40243     },
40244
40245     // private
40246     getParams : function(q){
40247         var p = {};
40248         //p[this.queryParam] = q;
40249         if(this.pageSize){
40250             p.start = 0;
40251             p.limit = this.pageSize;
40252         }
40253         return p;
40254     },
40255
40256     /**
40257      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40258      */
40259     collapse : function(){
40260         if(!this.isExpanded()){
40261             return;
40262         }
40263         this.list.hide();
40264         Roo.get(document).un('mousedown', this.collapseIf, this);
40265         Roo.get(document).un('mousewheel', this.collapseIf, this);
40266         if (!this.editable) {
40267             Roo.get(document).un('keydown', this.listKeyPress, this);
40268         }
40269         this.fireEvent('collapse', this);
40270     },
40271
40272     // private
40273     collapseIf : function(e){
40274         if(!e.within(this.wrap) && !e.within(this.list)){
40275             this.collapse();
40276         }
40277     },
40278
40279     /**
40280      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40281      */
40282     expand : function(){
40283         if(this.isExpanded() || !this.hasFocus){
40284             return;
40285         }
40286         this.list.alignTo(this.el, this.listAlign);
40287         this.list.show();
40288         Roo.get(document).on('mousedown', this.collapseIf, this);
40289         Roo.get(document).on('mousewheel', this.collapseIf, this);
40290         if (!this.editable) {
40291             Roo.get(document).on('keydown', this.listKeyPress, this);
40292         }
40293         
40294         this.fireEvent('expand', this);
40295     },
40296
40297     // private
40298     // Implements the default empty TriggerField.onTriggerClick function
40299     onTriggerClick : function(){
40300         if(this.disabled){
40301             return;
40302         }
40303         if(this.isExpanded()){
40304             this.collapse();
40305             if (!this.blockFocus) {
40306                 this.el.focus();
40307             }
40308             
40309         }else {
40310             this.hasFocus = true;
40311             if(this.triggerAction == 'all') {
40312                 this.doQuery(this.allQuery, true);
40313             } else {
40314                 this.doQuery(this.getRawValue());
40315             }
40316             if (!this.blockFocus) {
40317                 this.el.focus();
40318             }
40319         }
40320     },
40321     listKeyPress : function(e)
40322     {
40323         //Roo.log('listkeypress');
40324         // scroll to first matching element based on key pres..
40325         if (e.isSpecialKey()) {
40326             return false;
40327         }
40328         var k = String.fromCharCode(e.getKey()).toUpperCase();
40329         //Roo.log(k);
40330         var match  = false;
40331         var csel = this.view.getSelectedNodes();
40332         var cselitem = false;
40333         if (csel.length) {
40334             var ix = this.view.indexOf(csel[0]);
40335             cselitem  = this.store.getAt(ix);
40336             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40337                 cselitem = false;
40338             }
40339             
40340         }
40341         
40342         this.store.each(function(v) { 
40343             if (cselitem) {
40344                 // start at existing selection.
40345                 if (cselitem.id == v.id) {
40346                     cselitem = false;
40347                 }
40348                 return;
40349             }
40350                 
40351             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40352                 match = this.store.indexOf(v);
40353                 return false;
40354             }
40355         }, this);
40356         
40357         if (match === false) {
40358             return true; // no more action?
40359         }
40360         // scroll to?
40361         this.view.select(match);
40362         var sn = Roo.get(this.view.getSelectedNodes()[0])
40363         sn.scrollIntoView(sn.dom.parentNode, false);
40364     }
40365
40366     /** 
40367     * @cfg {Boolean} grow 
40368     * @hide 
40369     */
40370     /** 
40371     * @cfg {Number} growMin 
40372     * @hide 
40373     */
40374     /** 
40375     * @cfg {Number} growMax 
40376     * @hide 
40377     */
40378     /**
40379      * @hide
40380      * @method autoSize
40381      */
40382 });/*
40383  * Copyright(c) 2010-2012, Roo J Solutions Limited
40384  *
40385  * Licence LGPL
40386  *
40387  */
40388
40389 /**
40390  * @class Roo.form.ComboBoxArray
40391  * @extends Roo.form.TextField
40392  * A facebook style adder... for lists of email / people / countries  etc...
40393  * pick multiple items from a combo box, and shows each one.
40394  *
40395  *  Fred [x]  Brian [x]  [Pick another |v]
40396  *
40397  *
40398  *  For this to work: it needs various extra information
40399  *    - normal combo problay has
40400  *      name, hiddenName
40401  *    + displayField, valueField
40402  *
40403  *    For our purpose...
40404  *
40405  *
40406  *   If we change from 'extends' to wrapping...
40407  *   
40408  *  
40409  *
40410  
40411  
40412  * @constructor
40413  * Create a new ComboBoxArray.
40414  * @param {Object} config Configuration options
40415  */
40416  
40417
40418 Roo.form.ComboBoxArray = function(config)
40419 {
40420     this.addEvents({
40421         /**
40422          * @event beforeremove
40423          * Fires before remove the value from the list
40424              * @param {Roo.form.ComboBoxArray} _self This combo box array
40425              * @param {Roo.form.ComboBoxArray.Item} item removed item
40426              */
40427         'beforeremove' : true,
40428         /**
40429          * @event remove
40430          * Fires when remove the value from the list
40431              * @param {Roo.form.ComboBoxArray} _self This combo box array
40432              * @param {Roo.form.ComboBoxArray.Item} item removed item
40433              */
40434         'remove' : true
40435         
40436         
40437     });
40438     
40439     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40440     
40441     this.items = new Roo.util.MixedCollection(false);
40442     
40443     // construct the child combo...
40444     
40445     
40446     
40447     
40448    
40449     
40450 }
40451
40452  
40453 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40454
40455     /**
40456      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40457      */
40458     
40459     lastData : false,
40460     
40461     // behavies liek a hiddne field
40462     inputType:      'hidden',
40463     /**
40464      * @cfg {Number} width The width of the box that displays the selected element
40465      */ 
40466     width:          300,
40467
40468     
40469     
40470     /**
40471      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40472      */
40473     name : false,
40474     /**
40475      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40476      */
40477     hiddenName : false,
40478     
40479     
40480     // private the array of items that are displayed..
40481     items  : false,
40482     // private - the hidden field el.
40483     hiddenEl : false,
40484     // private - the filed el..
40485     el : false,
40486     
40487     //validateValue : function() { return true; }, // all values are ok!
40488     //onAddClick: function() { },
40489     
40490     onRender : function(ct, position) 
40491     {
40492         
40493         // create the standard hidden element
40494         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40495         
40496         
40497         // give fake names to child combo;
40498         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40499         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40500         
40501         this.combo = Roo.factory(this.combo, Roo.form);
40502         this.combo.onRender(ct, position);
40503         if (typeof(this.combo.width) != 'undefined') {
40504             this.combo.onResize(this.combo.width,0);
40505         }
40506         
40507         this.combo.initEvents();
40508         
40509         // assigned so form know we need to do this..
40510         this.store          = this.combo.store;
40511         this.valueField     = this.combo.valueField;
40512         this.displayField   = this.combo.displayField ;
40513         
40514         
40515         this.combo.wrap.addClass('x-cbarray-grp');
40516         
40517         var cbwrap = this.combo.wrap.createChild(
40518             {tag: 'div', cls: 'x-cbarray-cb'},
40519             this.combo.el.dom
40520         );
40521         
40522              
40523         this.hiddenEl = this.combo.wrap.createChild({
40524             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40525         });
40526         this.el = this.combo.wrap.createChild({
40527             tag: 'input',  type:'hidden' , name: this.name, value : ''
40528         });
40529          //   this.el.dom.removeAttribute("name");
40530         
40531         
40532         this.outerWrap = this.combo.wrap;
40533         this.wrap = cbwrap;
40534         
40535         this.outerWrap.setWidth(this.width);
40536         this.outerWrap.dom.removeChild(this.el.dom);
40537         
40538         this.wrap.dom.appendChild(this.el.dom);
40539         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40540         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40541         
40542         this.combo.trigger.setStyle('position','relative');
40543         this.combo.trigger.setStyle('left', '0px');
40544         this.combo.trigger.setStyle('top', '2px');
40545         
40546         this.combo.el.setStyle('vertical-align', 'text-bottom');
40547         
40548         //this.trigger.setStyle('vertical-align', 'top');
40549         
40550         // this should use the code from combo really... on('add' ....)
40551         if (this.adder) {
40552             
40553         
40554             this.adder = this.outerWrap.createChild(
40555                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40556             var _t = this;
40557             this.adder.on('click', function(e) {
40558                 _t.fireEvent('adderclick', this, e);
40559             }, _t);
40560         }
40561         //var _t = this;
40562         //this.adder.on('click', this.onAddClick, _t);
40563         
40564         
40565         this.combo.on('select', function(cb, rec, ix) {
40566             this.addItem(rec.data);
40567             
40568             cb.setValue('');
40569             cb.el.dom.value = '';
40570             //cb.lastData = rec.data;
40571             // add to list
40572             
40573         }, this);
40574         
40575         
40576     },
40577     
40578     
40579     getName: function()
40580     {
40581         // returns hidden if it's set..
40582         if (!this.rendered) {return ''};
40583         return  this.hiddenName ? this.hiddenName : this.name;
40584         
40585     },
40586     
40587     
40588     onResize: function(w, h){
40589         
40590         return;
40591         // not sure if this is needed..
40592         //this.combo.onResize(w,h);
40593         
40594         if(typeof w != 'number'){
40595             // we do not handle it!?!?
40596             return;
40597         }
40598         var tw = this.combo.trigger.getWidth();
40599         tw += this.addicon ? this.addicon.getWidth() : 0;
40600         tw += this.editicon ? this.editicon.getWidth() : 0;
40601         var x = w - tw;
40602         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40603             
40604         this.combo.trigger.setStyle('left', '0px');
40605         
40606         if(this.list && this.listWidth === undefined){
40607             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40608             this.list.setWidth(lw);
40609             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40610         }
40611         
40612     
40613         
40614     },
40615     
40616     addItem: function(rec)
40617     {
40618         var valueField = this.combo.valueField;
40619         var displayField = this.combo.displayField;
40620         if (this.items.indexOfKey(rec[valueField]) > -1) {
40621             //console.log("GOT " + rec.data.id);
40622             return;
40623         }
40624         
40625         var x = new Roo.form.ComboBoxArray.Item({
40626             //id : rec[this.idField],
40627             data : rec,
40628             displayField : displayField ,
40629             tipField : displayField ,
40630             cb : this
40631         });
40632         // use the 
40633         this.items.add(rec[valueField],x);
40634         // add it before the element..
40635         this.updateHiddenEl();
40636         x.render(this.outerWrap, this.wrap.dom);
40637         // add the image handler..
40638     },
40639     
40640     updateHiddenEl : function()
40641     {
40642         this.validate();
40643         if (!this.hiddenEl) {
40644             return;
40645         }
40646         var ar = [];
40647         var idField = this.combo.valueField;
40648         
40649         this.items.each(function(f) {
40650             ar.push(f.data[idField]);
40651            
40652         });
40653         this.hiddenEl.dom.value = ar.join(',');
40654         this.validate();
40655     },
40656     
40657     reset : function()
40658     {
40659         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40660         this.items.each(function(f) {
40661            f.remove(); 
40662         });
40663         this.el.dom.value = '';
40664         if (this.hiddenEl) {
40665             this.hiddenEl.dom.value = '';
40666         }
40667         
40668     },
40669     getValue: function()
40670     {
40671         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40672     },
40673     setValue: function(v) // not a valid action - must use addItems..
40674     {
40675          
40676         this.reset();
40677         
40678         
40679         
40680         if (this.store.isLocal && (typeof(v) == 'string')) {
40681             // then we can use the store to find the values..
40682             // comma seperated at present.. this needs to allow JSON based encoding..
40683             this.hiddenEl.value  = v;
40684             var v_ar = [];
40685             Roo.each(v.split(','), function(k) {
40686                 Roo.log("CHECK " + this.valueField + ',' + k);
40687                 var li = this.store.query(this.valueField, k);
40688                 if (!li.length) {
40689                     return;
40690                 }
40691                 var add = {};
40692                 add[this.valueField] = k;
40693                 add[this.displayField] = li.item(0).data[this.displayField];
40694                 
40695                 this.addItem(add);
40696             }, this) 
40697              
40698         }
40699         if (typeof(v) == 'object' ) {
40700             // then let's assume it's an array of objects..
40701             Roo.each(v, function(l) {
40702                 this.addItem(l);
40703             }, this);
40704              
40705         }
40706         
40707         
40708     },
40709     setFromData: function(v)
40710     {
40711         // this recieves an object, if setValues is called.
40712         this.reset();
40713         this.el.dom.value = v[this.displayField];
40714         this.hiddenEl.dom.value = v[this.valueField];
40715         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40716             return;
40717         }
40718         var kv = v[this.valueField];
40719         var dv = v[this.displayField];
40720         kv = typeof(kv) != 'string' ? '' : kv;
40721         dv = typeof(dv) != 'string' ? '' : dv;
40722         
40723         
40724         var keys = kv.split(',');
40725         var display = dv.split(',');
40726         for (var i = 0 ; i < keys.length; i++) {
40727             
40728             add = {};
40729             add[this.valueField] = keys[i];
40730             add[this.displayField] = display[i];
40731             this.addItem(add);
40732         }
40733       
40734         
40735     },
40736     
40737     /**
40738      * Validates the combox array value
40739      * @return {Boolean} True if the value is valid, else false
40740      */
40741     validate : function(){
40742         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40743             this.clearInvalid();
40744             return true;
40745         }
40746         return false;
40747     },
40748     
40749     validateValue : function(value){
40750         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40751         
40752     },
40753     
40754     /*@
40755      * overide
40756      * 
40757      */
40758     isDirty : function() {
40759         if(this.disabled) {
40760             return false;
40761         }
40762         
40763         try {
40764             var d = Roo.decode(String(this.originalValue));
40765         } catch (e) {
40766             return String(this.getValue()) !== String(this.originalValue);
40767         }
40768         
40769         var originalValue = [];
40770         
40771         for (var i = 0; i < d.length; i++){
40772             originalValue.push(d[i][this.valueField]);
40773         }
40774         
40775         return String(this.getValue()) !== String(originalValue.join(','));
40776         
40777     }
40778     
40779 });
40780
40781
40782
40783 /**
40784  * @class Roo.form.ComboBoxArray.Item
40785  * @extends Roo.BoxComponent
40786  * A selected item in the list
40787  *  Fred [x]  Brian [x]  [Pick another |v]
40788  * 
40789  * @constructor
40790  * Create a new item.
40791  * @param {Object} config Configuration options
40792  */
40793  
40794 Roo.form.ComboBoxArray.Item = function(config) {
40795     config.id = Roo.id();
40796     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40797 }
40798
40799 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40800     data : {},
40801     cb: false,
40802     displayField : false,
40803     tipField : false,
40804     
40805     
40806     defaultAutoCreate : {
40807         tag: 'div',
40808         cls: 'x-cbarray-item',
40809         cn : [ 
40810             { tag: 'div' },
40811             {
40812                 tag: 'img',
40813                 width:16,
40814                 height : 16,
40815                 src : Roo.BLANK_IMAGE_URL ,
40816                 align: 'center'
40817             }
40818         ]
40819         
40820     },
40821     
40822  
40823     onRender : function(ct, position)
40824     {
40825         Roo.form.Field.superclass.onRender.call(this, ct, position);
40826         
40827         if(!this.el){
40828             var cfg = this.getAutoCreate();
40829             this.el = ct.createChild(cfg, position);
40830         }
40831         
40832         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40833         
40834         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40835             this.cb.renderer(this.data) :
40836             String.format('{0}',this.data[this.displayField]);
40837         
40838             
40839         this.el.child('div').dom.setAttribute('qtip',
40840                         String.format('{0}',this.data[this.tipField])
40841         );
40842         
40843         this.el.child('img').on('click', this.remove, this);
40844         
40845     },
40846    
40847     remove : function()
40848     {
40849         if(this.cb.disabled){
40850             return;
40851         }
40852         
40853         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40854             this.cb.items.remove(this);
40855             this.el.child('img').un('click', this.remove, this);
40856             this.el.remove();
40857             this.cb.updateHiddenEl();
40858
40859             this.cb.fireEvent('remove', this.cb, this);
40860         }
40861         
40862     }
40863 });/*
40864  * Based on:
40865  * Ext JS Library 1.1.1
40866  * Copyright(c) 2006-2007, Ext JS, LLC.
40867  *
40868  * Originally Released Under LGPL - original licence link has changed is not relivant.
40869  *
40870  * Fork - LGPL
40871  * <script type="text/javascript">
40872  */
40873 /**
40874  * @class Roo.form.Checkbox
40875  * @extends Roo.form.Field
40876  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40877  * @constructor
40878  * Creates a new Checkbox
40879  * @param {Object} config Configuration options
40880  */
40881 Roo.form.Checkbox = function(config){
40882     Roo.form.Checkbox.superclass.constructor.call(this, config);
40883     this.addEvents({
40884         /**
40885          * @event check
40886          * Fires when the checkbox is checked or unchecked.
40887              * @param {Roo.form.Checkbox} this This checkbox
40888              * @param {Boolean} checked The new checked value
40889              */
40890         check : true
40891     });
40892 };
40893
40894 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40895     /**
40896      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40897      */
40898     focusClass : undefined,
40899     /**
40900      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40901      */
40902     fieldClass: "x-form-field",
40903     /**
40904      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40905      */
40906     checked: false,
40907     /**
40908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40909      * {tag: "input", type: "checkbox", autocomplete: "off"})
40910      */
40911     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40912     /**
40913      * @cfg {String} boxLabel The text that appears beside the checkbox
40914      */
40915     boxLabel : "",
40916     /**
40917      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40918      */  
40919     inputValue : '1',
40920     /**
40921      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40922      */
40923      valueOff: '0', // value when not checked..
40924
40925     actionMode : 'viewEl', 
40926     //
40927     // private
40928     itemCls : 'x-menu-check-item x-form-item',
40929     groupClass : 'x-menu-group-item',
40930     inputType : 'hidden',
40931     
40932     
40933     inSetChecked: false, // check that we are not calling self...
40934     
40935     inputElement: false, // real input element?
40936     basedOn: false, // ????
40937     
40938     isFormField: true, // not sure where this is needed!!!!
40939
40940     onResize : function(){
40941         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40942         if(!this.boxLabel){
40943             this.el.alignTo(this.wrap, 'c-c');
40944         }
40945     },
40946
40947     initEvents : function(){
40948         Roo.form.Checkbox.superclass.initEvents.call(this);
40949         this.el.on("click", this.onClick,  this);
40950         this.el.on("change", this.onClick,  this);
40951     },
40952
40953
40954     getResizeEl : function(){
40955         return this.wrap;
40956     },
40957
40958     getPositionEl : function(){
40959         return this.wrap;
40960     },
40961
40962     // private
40963     onRender : function(ct, position){
40964         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40965         /*
40966         if(this.inputValue !== undefined){
40967             this.el.dom.value = this.inputValue;
40968         }
40969         */
40970         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40971         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40972         var viewEl = this.wrap.createChild({ 
40973             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40974         this.viewEl = viewEl;   
40975         this.wrap.on('click', this.onClick,  this); 
40976         
40977         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40978         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40979         
40980         
40981         
40982         if(this.boxLabel){
40983             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40984         //    viewEl.on('click', this.onClick,  this); 
40985         }
40986         //if(this.checked){
40987             this.setChecked(this.checked);
40988         //}else{
40989             //this.checked = this.el.dom;
40990         //}
40991
40992     },
40993
40994     // private
40995     initValue : Roo.emptyFn,
40996
40997     /**
40998      * Returns the checked state of the checkbox.
40999      * @return {Boolean} True if checked, else false
41000      */
41001     getValue : function(){
41002         if(this.el){
41003             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
41004         }
41005         return this.valueOff;
41006         
41007     },
41008
41009         // private
41010     onClick : function(){ 
41011         if (this.disabled) {
41012             return;
41013         }
41014         this.setChecked(!this.checked);
41015
41016         //if(this.el.dom.checked != this.checked){
41017         //    this.setValue(this.el.dom.checked);
41018        // }
41019     },
41020
41021     /**
41022      * Sets the checked state of the checkbox.
41023      * On is always based on a string comparison between inputValue and the param.
41024      * @param {Boolean/String} value - the value to set 
41025      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
41026      */
41027     setValue : function(v,suppressEvent){
41028         
41029         
41030         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41031         //if(this.el && this.el.dom){
41032         //    this.el.dom.checked = this.checked;
41033         //    this.el.dom.defaultChecked = this.checked;
41034         //}
41035         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41036         //this.fireEvent("check", this, this.checked);
41037     },
41038     // private..
41039     setChecked : function(state,suppressEvent)
41040     {
41041         if (this.inSetChecked) {
41042             this.checked = state;
41043             return;
41044         }
41045         
41046     
41047         if(this.wrap){
41048             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41049         }
41050         this.checked = state;
41051         if(suppressEvent !== true){
41052             this.fireEvent('check', this, state);
41053         }
41054         this.inSetChecked = true;
41055         this.el.dom.value = state ? this.inputValue : this.valueOff;
41056         this.inSetChecked = false;
41057         
41058     },
41059     // handle setting of hidden value by some other method!!?!?
41060     setFromHidden: function()
41061     {
41062         if(!this.el){
41063             return;
41064         }
41065         //console.log("SET FROM HIDDEN");
41066         //alert('setFrom hidden');
41067         this.setValue(this.el.dom.value);
41068     },
41069     
41070     onDestroy : function()
41071     {
41072         if(this.viewEl){
41073             Roo.get(this.viewEl).remove();
41074         }
41075          
41076         Roo.form.Checkbox.superclass.onDestroy.call(this);
41077     }
41078
41079 });/*
41080  * Based on:
41081  * Ext JS Library 1.1.1
41082  * Copyright(c) 2006-2007, Ext JS, LLC.
41083  *
41084  * Originally Released Under LGPL - original licence link has changed is not relivant.
41085  *
41086  * Fork - LGPL
41087  * <script type="text/javascript">
41088  */
41089  
41090 /**
41091  * @class Roo.form.Radio
41092  * @extends Roo.form.Checkbox
41093  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41094  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41095  * @constructor
41096  * Creates a new Radio
41097  * @param {Object} config Configuration options
41098  */
41099 Roo.form.Radio = function(){
41100     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41101 };
41102 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41103     inputType: 'radio',
41104
41105     /**
41106      * If this radio is part of a group, it will return the selected value
41107      * @return {String}
41108      */
41109     getGroupValue : function(){
41110         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41111     },
41112     
41113     
41114     onRender : function(ct, position){
41115         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41116         
41117         if(this.inputValue !== undefined){
41118             this.el.dom.value = this.inputValue;
41119         }
41120          
41121         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41122         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41123         //var viewEl = this.wrap.createChild({ 
41124         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41125         //this.viewEl = viewEl;   
41126         //this.wrap.on('click', this.onClick,  this); 
41127         
41128         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41129         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41130         
41131         
41132         
41133         if(this.boxLabel){
41134             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41135         //    viewEl.on('click', this.onClick,  this); 
41136         }
41137          if(this.checked){
41138             this.el.dom.checked =   'checked' ;
41139         }
41140          
41141     } 
41142     
41143     
41144 });//<script type="text/javascript">
41145
41146 /*
41147  * Based  Ext JS Library 1.1.1
41148  * Copyright(c) 2006-2007, Ext JS, LLC.
41149  * LGPL
41150  *
41151  */
41152  
41153 /**
41154  * @class Roo.HtmlEditorCore
41155  * @extends Roo.Component
41156  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41157  *
41158  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41159  */
41160
41161 Roo.HtmlEditorCore = function(config){
41162     
41163     
41164     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41165     
41166     
41167     this.addEvents({
41168         /**
41169          * @event initialize
41170          * Fires when the editor is fully initialized (including the iframe)
41171          * @param {Roo.HtmlEditorCore} this
41172          */
41173         initialize: true,
41174         /**
41175          * @event activate
41176          * Fires when the editor is first receives the focus. Any insertion must wait
41177          * until after this event.
41178          * @param {Roo.HtmlEditorCore} this
41179          */
41180         activate: true,
41181          /**
41182          * @event beforesync
41183          * Fires before the textarea is updated with content from the editor iframe. Return false
41184          * to cancel the sync.
41185          * @param {Roo.HtmlEditorCore} this
41186          * @param {String} html
41187          */
41188         beforesync: true,
41189          /**
41190          * @event beforepush
41191          * Fires before the iframe editor is updated with content from the textarea. Return false
41192          * to cancel the push.
41193          * @param {Roo.HtmlEditorCore} this
41194          * @param {String} html
41195          */
41196         beforepush: true,
41197          /**
41198          * @event sync
41199          * Fires when the textarea is updated with content from the editor iframe.
41200          * @param {Roo.HtmlEditorCore} this
41201          * @param {String} html
41202          */
41203         sync: true,
41204          /**
41205          * @event push
41206          * Fires when the iframe editor is updated with content from the textarea.
41207          * @param {Roo.HtmlEditorCore} this
41208          * @param {String} html
41209          */
41210         push: true,
41211         
41212         /**
41213          * @event editorevent
41214          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41215          * @param {Roo.HtmlEditorCore} this
41216          */
41217         editorevent: true
41218         
41219     });
41220     
41221     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41222     
41223     // defaults : white / black...
41224     this.applyBlacklists();
41225     
41226     
41227     
41228 };
41229
41230
41231 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41232
41233
41234      /**
41235      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41236      */
41237     
41238     owner : false,
41239     
41240      /**
41241      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41242      *                        Roo.resizable.
41243      */
41244     resizable : false,
41245      /**
41246      * @cfg {Number} height (in pixels)
41247      */   
41248     height: 300,
41249    /**
41250      * @cfg {Number} width (in pixels)
41251      */   
41252     width: 500,
41253     
41254     /**
41255      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41256      * 
41257      */
41258     stylesheets: false,
41259     
41260     // id of frame..
41261     frameId: false,
41262     
41263     // private properties
41264     validationEvent : false,
41265     deferHeight: true,
41266     initialized : false,
41267     activated : false,
41268     sourceEditMode : false,
41269     onFocus : Roo.emptyFn,
41270     iframePad:3,
41271     hideMode:'offsets',
41272     
41273     clearUp: true,
41274     
41275     // blacklist + whitelisted elements..
41276     black: false,
41277     white: false,
41278      
41279     
41280
41281     /**
41282      * Protected method that will not generally be called directly. It
41283      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41284      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41285      */
41286     getDocMarkup : function(){
41287         // body styles..
41288         var st = '';
41289         
41290         // inherit styels from page...?? 
41291         if (this.stylesheets === false) {
41292             
41293             Roo.get(document.head).select('style').each(function(node) {
41294                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41295             });
41296             
41297             Roo.get(document.head).select('link').each(function(node) { 
41298                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41299             });
41300             
41301         } else if (!this.stylesheets.length) {
41302                 // simple..
41303                 st = '<style type="text/css">' +
41304                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41305                    '</style>';
41306         } else { 
41307             
41308         }
41309         
41310         st +=  '<style type="text/css">' +
41311             'IMG { cursor: pointer } ' +
41312         '</style>';
41313
41314         
41315         return '<html><head>' + st  +
41316             //<style type="text/css">' +
41317             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41318             //'</style>' +
41319             ' </head><body class="roo-htmleditor-body"></body></html>';
41320     },
41321
41322     // private
41323     onRender : function(ct, position)
41324     {
41325         var _t = this;
41326         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41327         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41328         
41329         
41330         this.el.dom.style.border = '0 none';
41331         this.el.dom.setAttribute('tabIndex', -1);
41332         this.el.addClass('x-hidden hide');
41333         
41334         
41335         
41336         if(Roo.isIE){ // fix IE 1px bogus margin
41337             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41338         }
41339        
41340         
41341         this.frameId = Roo.id();
41342         
41343          
41344         
41345         var iframe = this.owner.wrap.createChild({
41346             tag: 'iframe',
41347             cls: 'form-control', // bootstrap..
41348             id: this.frameId,
41349             name: this.frameId,
41350             frameBorder : 'no',
41351             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41352         }, this.el
41353         );
41354         
41355         
41356         this.iframe = iframe.dom;
41357
41358          this.assignDocWin();
41359         
41360         this.doc.designMode = 'on';
41361        
41362         this.doc.open();
41363         this.doc.write(this.getDocMarkup());
41364         this.doc.close();
41365
41366         
41367         var task = { // must defer to wait for browser to be ready
41368             run : function(){
41369                 //console.log("run task?" + this.doc.readyState);
41370                 this.assignDocWin();
41371                 if(this.doc.body || this.doc.readyState == 'complete'){
41372                     try {
41373                         this.doc.designMode="on";
41374                     } catch (e) {
41375                         return;
41376                     }
41377                     Roo.TaskMgr.stop(task);
41378                     this.initEditor.defer(10, this);
41379                 }
41380             },
41381             interval : 10,
41382             duration: 10000,
41383             scope: this
41384         };
41385         Roo.TaskMgr.start(task);
41386
41387     },
41388
41389     // private
41390     onResize : function(w, h)
41391     {
41392          Roo.log('resize: ' +w + ',' + h );
41393         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41394         if(!this.iframe){
41395             return;
41396         }
41397         if(typeof w == 'number'){
41398             
41399             this.iframe.style.width = w + 'px';
41400         }
41401         if(typeof h == 'number'){
41402             
41403             this.iframe.style.height = h + 'px';
41404             if(this.doc){
41405                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41406             }
41407         }
41408         
41409     },
41410
41411     /**
41412      * Toggles the editor between standard and source edit mode.
41413      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41414      */
41415     toggleSourceEdit : function(sourceEditMode){
41416         
41417         this.sourceEditMode = sourceEditMode === true;
41418         
41419         if(this.sourceEditMode){
41420  
41421             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41422             
41423         }else{
41424             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41425             //this.iframe.className = '';
41426             this.deferFocus();
41427         }
41428         //this.setSize(this.owner.wrap.getSize());
41429         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41430     },
41431
41432     
41433   
41434
41435     /**
41436      * Protected method that will not generally be called directly. If you need/want
41437      * custom HTML cleanup, this is the method you should override.
41438      * @param {String} html The HTML to be cleaned
41439      * return {String} The cleaned HTML
41440      */
41441     cleanHtml : function(html){
41442         html = String(html);
41443         if(html.length > 5){
41444             if(Roo.isSafari){ // strip safari nonsense
41445                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41446             }
41447         }
41448         if(html == '&nbsp;'){
41449             html = '';
41450         }
41451         return html;
41452     },
41453
41454     /**
41455      * HTML Editor -> Textarea
41456      * Protected method that will not generally be called directly. Syncs the contents
41457      * of the editor iframe with the textarea.
41458      */
41459     syncValue : function(){
41460         if(this.initialized){
41461             var bd = (this.doc.body || this.doc.documentElement);
41462             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41463             var html = bd.innerHTML;
41464             if(Roo.isSafari){
41465                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41466                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41467                 if(m && m[1]){
41468                     html = '<div style="'+m[0]+'">' + html + '</div>';
41469                 }
41470             }
41471             html = this.cleanHtml(html);
41472             // fix up the special chars.. normaly like back quotes in word...
41473             // however we do not want to do this with chinese..
41474             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41475                 var cc = b.charCodeAt();
41476                 if (
41477                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41478                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41479                     (cc >= 0xf900 && cc < 0xfb00 )
41480                 ) {
41481                         return b;
41482                 }
41483                 return "&#"+cc+";" 
41484             });
41485             if(this.owner.fireEvent('beforesync', this, html) !== false){
41486                 this.el.dom.value = html;
41487                 this.owner.fireEvent('sync', this, html);
41488             }
41489         }
41490     },
41491
41492     /**
41493      * Protected method that will not generally be called directly. Pushes the value of the textarea
41494      * into the iframe editor.
41495      */
41496     pushValue : function(){
41497         if(this.initialized){
41498             var v = this.el.dom.value.trim();
41499             
41500 //            if(v.length < 1){
41501 //                v = '&#160;';
41502 //            }
41503             
41504             if(this.owner.fireEvent('beforepush', this, v) !== false){
41505                 var d = (this.doc.body || this.doc.documentElement);
41506                 d.innerHTML = v;
41507                 this.cleanUpPaste();
41508                 this.el.dom.value = d.innerHTML;
41509                 this.owner.fireEvent('push', this, v);
41510             }
41511         }
41512     },
41513
41514     // private
41515     deferFocus : function(){
41516         this.focus.defer(10, this);
41517     },
41518
41519     // doc'ed in Field
41520     focus : function(){
41521         if(this.win && !this.sourceEditMode){
41522             this.win.focus();
41523         }else{
41524             this.el.focus();
41525         }
41526     },
41527     
41528     assignDocWin: function()
41529     {
41530         var iframe = this.iframe;
41531         
41532          if(Roo.isIE){
41533             this.doc = iframe.contentWindow.document;
41534             this.win = iframe.contentWindow;
41535         } else {
41536 //            if (!Roo.get(this.frameId)) {
41537 //                return;
41538 //            }
41539 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41540 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41541             
41542             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41543                 return;
41544             }
41545             
41546             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41547             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41548         }
41549     },
41550     
41551     // private
41552     initEditor : function(){
41553         //console.log("INIT EDITOR");
41554         this.assignDocWin();
41555         
41556         
41557         
41558         this.doc.designMode="on";
41559         this.doc.open();
41560         this.doc.write(this.getDocMarkup());
41561         this.doc.close();
41562         
41563         var dbody = (this.doc.body || this.doc.documentElement);
41564         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41565         // this copies styles from the containing element into thsi one..
41566         // not sure why we need all of this..
41567         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41568         
41569         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41570         //ss['background-attachment'] = 'fixed'; // w3c
41571         dbody.bgProperties = 'fixed'; // ie
41572         //Roo.DomHelper.applyStyles(dbody, ss);
41573         Roo.EventManager.on(this.doc, {
41574             //'mousedown': this.onEditorEvent,
41575             'mouseup': this.onEditorEvent,
41576             'dblclick': this.onEditorEvent,
41577             'click': this.onEditorEvent,
41578             'keyup': this.onEditorEvent,
41579             buffer:100,
41580             scope: this
41581         });
41582         if(Roo.isGecko){
41583             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41584         }
41585         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41586             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41587         }
41588         this.initialized = true;
41589
41590         this.owner.fireEvent('initialize', this);
41591         this.pushValue();
41592     },
41593
41594     // private
41595     onDestroy : function(){
41596         
41597         
41598         
41599         if(this.rendered){
41600             
41601             //for (var i =0; i < this.toolbars.length;i++) {
41602             //    // fixme - ask toolbars for heights?
41603             //    this.toolbars[i].onDestroy();
41604            // }
41605             
41606             //this.wrap.dom.innerHTML = '';
41607             //this.wrap.remove();
41608         }
41609     },
41610
41611     // private
41612     onFirstFocus : function(){
41613         
41614         this.assignDocWin();
41615         
41616         
41617         this.activated = true;
41618          
41619     
41620         if(Roo.isGecko){ // prevent silly gecko errors
41621             this.win.focus();
41622             var s = this.win.getSelection();
41623             if(!s.focusNode || s.focusNode.nodeType != 3){
41624                 var r = s.getRangeAt(0);
41625                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41626                 r.collapse(true);
41627                 this.deferFocus();
41628             }
41629             try{
41630                 this.execCmd('useCSS', true);
41631                 this.execCmd('styleWithCSS', false);
41632             }catch(e){}
41633         }
41634         this.owner.fireEvent('activate', this);
41635     },
41636
41637     // private
41638     adjustFont: function(btn){
41639         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41640         //if(Roo.isSafari){ // safari
41641         //    adjust *= 2;
41642        // }
41643         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41644         if(Roo.isSafari){ // safari
41645             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41646             v =  (v < 10) ? 10 : v;
41647             v =  (v > 48) ? 48 : v;
41648             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41649             
41650         }
41651         
41652         
41653         v = Math.max(1, v+adjust);
41654         
41655         this.execCmd('FontSize', v  );
41656     },
41657
41658     onEditorEvent : function(e){
41659         this.owner.fireEvent('editorevent', this, e);
41660       //  this.updateToolbar();
41661         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41662     },
41663
41664     insertTag : function(tg)
41665     {
41666         // could be a bit smarter... -> wrap the current selected tRoo..
41667         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41668             
41669             range = this.createRange(this.getSelection());
41670             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41671             wrappingNode.appendChild(range.extractContents());
41672             range.insertNode(wrappingNode);
41673
41674             return;
41675             
41676             
41677             
41678         }
41679         this.execCmd("formatblock",   tg);
41680         
41681     },
41682     
41683     insertText : function(txt)
41684     {
41685         
41686         
41687         var range = this.createRange();
41688         range.deleteContents();
41689                //alert(Sender.getAttribute('label'));
41690                
41691         range.insertNode(this.doc.createTextNode(txt));
41692     } ,
41693     
41694      
41695
41696     /**
41697      * Executes a Midas editor command on the editor document and performs necessary focus and
41698      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41699      * @param {String} cmd The Midas command
41700      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41701      */
41702     relayCmd : function(cmd, value){
41703         this.win.focus();
41704         this.execCmd(cmd, value);
41705         this.owner.fireEvent('editorevent', this);
41706         //this.updateToolbar();
41707         this.owner.deferFocus();
41708     },
41709
41710     /**
41711      * Executes a Midas editor command directly on the editor document.
41712      * For visual commands, you should use {@link #relayCmd} instead.
41713      * <b>This should only be called after the editor is initialized.</b>
41714      * @param {String} cmd The Midas command
41715      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41716      */
41717     execCmd : function(cmd, value){
41718         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41719         this.syncValue();
41720     },
41721  
41722  
41723    
41724     /**
41725      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41726      * to insert tRoo.
41727      * @param {String} text | dom node.. 
41728      */
41729     insertAtCursor : function(text)
41730     {
41731         
41732         
41733         
41734         if(!this.activated){
41735             return;
41736         }
41737         /*
41738         if(Roo.isIE){
41739             this.win.focus();
41740             var r = this.doc.selection.createRange();
41741             if(r){
41742                 r.collapse(true);
41743                 r.pasteHTML(text);
41744                 this.syncValue();
41745                 this.deferFocus();
41746             
41747             }
41748             return;
41749         }
41750         */
41751         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41752             this.win.focus();
41753             
41754             
41755             // from jquery ui (MIT licenced)
41756             var range, node;
41757             var win = this.win;
41758             
41759             if (win.getSelection && win.getSelection().getRangeAt) {
41760                 range = win.getSelection().getRangeAt(0);
41761                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41762                 range.insertNode(node);
41763             } else if (win.document.selection && win.document.selection.createRange) {
41764                 // no firefox support
41765                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41766                 win.document.selection.createRange().pasteHTML(txt);
41767             } else {
41768                 // no firefox support
41769                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41770                 this.execCmd('InsertHTML', txt);
41771             } 
41772             
41773             this.syncValue();
41774             
41775             this.deferFocus();
41776         }
41777     },
41778  // private
41779     mozKeyPress : function(e){
41780         if(e.ctrlKey){
41781             var c = e.getCharCode(), cmd;
41782           
41783             if(c > 0){
41784                 c = String.fromCharCode(c).toLowerCase();
41785                 switch(c){
41786                     case 'b':
41787                         cmd = 'bold';
41788                         break;
41789                     case 'i':
41790                         cmd = 'italic';
41791                         break;
41792                     
41793                     case 'u':
41794                         cmd = 'underline';
41795                         break;
41796                     
41797                     case 'v':
41798                         this.cleanUpPaste.defer(100, this);
41799                         return;
41800                         
41801                 }
41802                 if(cmd){
41803                     this.win.focus();
41804                     this.execCmd(cmd);
41805                     this.deferFocus();
41806                     e.preventDefault();
41807                 }
41808                 
41809             }
41810         }
41811     },
41812
41813     // private
41814     fixKeys : function(){ // load time branching for fastest keydown performance
41815         if(Roo.isIE){
41816             return function(e){
41817                 var k = e.getKey(), r;
41818                 if(k == e.TAB){
41819                     e.stopEvent();
41820                     r = this.doc.selection.createRange();
41821                     if(r){
41822                         r.collapse(true);
41823                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41824                         this.deferFocus();
41825                     }
41826                     return;
41827                 }
41828                 
41829                 if(k == e.ENTER){
41830                     r = this.doc.selection.createRange();
41831                     if(r){
41832                         var target = r.parentElement();
41833                         if(!target || target.tagName.toLowerCase() != 'li'){
41834                             e.stopEvent();
41835                             r.pasteHTML('<br />');
41836                             r.collapse(false);
41837                             r.select();
41838                         }
41839                     }
41840                 }
41841                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41842                     this.cleanUpPaste.defer(100, this);
41843                     return;
41844                 }
41845                 
41846                 
41847             };
41848         }else if(Roo.isOpera){
41849             return function(e){
41850                 var k = e.getKey();
41851                 if(k == e.TAB){
41852                     e.stopEvent();
41853                     this.win.focus();
41854                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41855                     this.deferFocus();
41856                 }
41857                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41858                     this.cleanUpPaste.defer(100, this);
41859                     return;
41860                 }
41861                 
41862             };
41863         }else if(Roo.isSafari){
41864             return function(e){
41865                 var k = e.getKey();
41866                 
41867                 if(k == e.TAB){
41868                     e.stopEvent();
41869                     this.execCmd('InsertText','\t');
41870                     this.deferFocus();
41871                     return;
41872                 }
41873                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41874                     this.cleanUpPaste.defer(100, this);
41875                     return;
41876                 }
41877                 
41878              };
41879         }
41880     }(),
41881     
41882     getAllAncestors: function()
41883     {
41884         var p = this.getSelectedNode();
41885         var a = [];
41886         if (!p) {
41887             a.push(p); // push blank onto stack..
41888             p = this.getParentElement();
41889         }
41890         
41891         
41892         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41893             a.push(p);
41894             p = p.parentNode;
41895         }
41896         a.push(this.doc.body);
41897         return a;
41898     },
41899     lastSel : false,
41900     lastSelNode : false,
41901     
41902     
41903     getSelection : function() 
41904     {
41905         this.assignDocWin();
41906         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41907     },
41908     
41909     getSelectedNode: function() 
41910     {
41911         // this may only work on Gecko!!!
41912         
41913         // should we cache this!!!!
41914         
41915         
41916         
41917          
41918         var range = this.createRange(this.getSelection()).cloneRange();
41919         
41920         if (Roo.isIE) {
41921             var parent = range.parentElement();
41922             while (true) {
41923                 var testRange = range.duplicate();
41924                 testRange.moveToElementText(parent);
41925                 if (testRange.inRange(range)) {
41926                     break;
41927                 }
41928                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41929                     break;
41930                 }
41931                 parent = parent.parentElement;
41932             }
41933             return parent;
41934         }
41935         
41936         // is ancestor a text element.
41937         var ac =  range.commonAncestorContainer;
41938         if (ac.nodeType == 3) {
41939             ac = ac.parentNode;
41940         }
41941         
41942         var ar = ac.childNodes;
41943          
41944         var nodes = [];
41945         var other_nodes = [];
41946         var has_other_nodes = false;
41947         for (var i=0;i<ar.length;i++) {
41948             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41949                 continue;
41950             }
41951             // fullly contained node.
41952             
41953             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41954                 nodes.push(ar[i]);
41955                 continue;
41956             }
41957             
41958             // probably selected..
41959             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41960                 other_nodes.push(ar[i]);
41961                 continue;
41962             }
41963             // outer..
41964             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41965                 continue;
41966             }
41967             
41968             
41969             has_other_nodes = true;
41970         }
41971         if (!nodes.length && other_nodes.length) {
41972             nodes= other_nodes;
41973         }
41974         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41975             return false;
41976         }
41977         
41978         return nodes[0];
41979     },
41980     createRange: function(sel)
41981     {
41982         // this has strange effects when using with 
41983         // top toolbar - not sure if it's a great idea.
41984         //this.editor.contentWindow.focus();
41985         if (typeof sel != "undefined") {
41986             try {
41987                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41988             } catch(e) {
41989                 return this.doc.createRange();
41990             }
41991         } else {
41992             return this.doc.createRange();
41993         }
41994     },
41995     getParentElement: function()
41996     {
41997         
41998         this.assignDocWin();
41999         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
42000         
42001         var range = this.createRange(sel);
42002          
42003         try {
42004             var p = range.commonAncestorContainer;
42005             while (p.nodeType == 3) { // text node
42006                 p = p.parentNode;
42007             }
42008             return p;
42009         } catch (e) {
42010             return null;
42011         }
42012     
42013     },
42014     /***
42015      *
42016      * Range intersection.. the hard stuff...
42017      *  '-1' = before
42018      *  '0' = hits..
42019      *  '1' = after.
42020      *         [ -- selected range --- ]
42021      *   [fail]                        [fail]
42022      *
42023      *    basically..
42024      *      if end is before start or  hits it. fail.
42025      *      if start is after end or hits it fail.
42026      *
42027      *   if either hits (but other is outside. - then it's not 
42028      *   
42029      *    
42030      **/
42031     
42032     
42033     // @see http://www.thismuchiknow.co.uk/?p=64.
42034     rangeIntersectsNode : function(range, node)
42035     {
42036         var nodeRange = node.ownerDocument.createRange();
42037         try {
42038             nodeRange.selectNode(node);
42039         } catch (e) {
42040             nodeRange.selectNodeContents(node);
42041         }
42042     
42043         var rangeStartRange = range.cloneRange();
42044         rangeStartRange.collapse(true);
42045     
42046         var rangeEndRange = range.cloneRange();
42047         rangeEndRange.collapse(false);
42048     
42049         var nodeStartRange = nodeRange.cloneRange();
42050         nodeStartRange.collapse(true);
42051     
42052         var nodeEndRange = nodeRange.cloneRange();
42053         nodeEndRange.collapse(false);
42054     
42055         return rangeStartRange.compareBoundaryPoints(
42056                  Range.START_TO_START, nodeEndRange) == -1 &&
42057                rangeEndRange.compareBoundaryPoints(
42058                  Range.START_TO_START, nodeStartRange) == 1;
42059         
42060          
42061     },
42062     rangeCompareNode : function(range, node)
42063     {
42064         var nodeRange = node.ownerDocument.createRange();
42065         try {
42066             nodeRange.selectNode(node);
42067         } catch (e) {
42068             nodeRange.selectNodeContents(node);
42069         }
42070         
42071         
42072         range.collapse(true);
42073     
42074         nodeRange.collapse(true);
42075      
42076         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42077         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42078          
42079         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42080         
42081         var nodeIsBefore   =  ss == 1;
42082         var nodeIsAfter    = ee == -1;
42083         
42084         if (nodeIsBefore && nodeIsAfter)
42085             return 0; // outer
42086         if (!nodeIsBefore && nodeIsAfter)
42087             return 1; //right trailed.
42088         
42089         if (nodeIsBefore && !nodeIsAfter)
42090             return 2;  // left trailed.
42091         // fully contined.
42092         return 3;
42093     },
42094
42095     // private? - in a new class?
42096     cleanUpPaste :  function()
42097     {
42098         // cleans up the whole document..
42099         Roo.log('cleanuppaste');
42100         
42101         this.cleanUpChildren(this.doc.body);
42102         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42103         if (clean != this.doc.body.innerHTML) {
42104             this.doc.body.innerHTML = clean;
42105         }
42106         
42107     },
42108     
42109     cleanWordChars : function(input) {// change the chars to hex code
42110         var he = Roo.HtmlEditorCore;
42111         
42112         var output = input;
42113         Roo.each(he.swapCodes, function(sw) { 
42114             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42115             
42116             output = output.replace(swapper, sw[1]);
42117         });
42118         
42119         return output;
42120     },
42121     
42122     
42123     cleanUpChildren : function (n)
42124     {
42125         if (!n.childNodes.length) {
42126             return;
42127         }
42128         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42129            this.cleanUpChild(n.childNodes[i]);
42130         }
42131     },
42132     
42133     
42134         
42135     
42136     cleanUpChild : function (node)
42137     {
42138         var ed = this;
42139         //console.log(node);
42140         if (node.nodeName == "#text") {
42141             // clean up silly Windows -- stuff?
42142             return; 
42143         }
42144         if (node.nodeName == "#comment") {
42145             node.parentNode.removeChild(node);
42146             // clean up silly Windows -- stuff?
42147             return; 
42148         }
42149         var lcname = node.tagName.toLowerCase();
42150         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42151         // whitelist of tags..
42152         
42153         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42154             // remove node.
42155             node.parentNode.removeChild(node);
42156             return;
42157             
42158         }
42159         
42160         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42161         
42162         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42163         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42164         
42165         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42166         //    remove_keep_children = true;
42167         //}
42168         
42169         if (remove_keep_children) {
42170             this.cleanUpChildren(node);
42171             // inserts everything just before this node...
42172             while (node.childNodes.length) {
42173                 var cn = node.childNodes[0];
42174                 node.removeChild(cn);
42175                 node.parentNode.insertBefore(cn, node);
42176             }
42177             node.parentNode.removeChild(node);
42178             return;
42179         }
42180         
42181         if (!node.attributes || !node.attributes.length) {
42182             this.cleanUpChildren(node);
42183             return;
42184         }
42185         
42186         function cleanAttr(n,v)
42187         {
42188             
42189             if (v.match(/^\./) || v.match(/^\//)) {
42190                 return;
42191             }
42192             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42193                 return;
42194             }
42195             if (v.match(/^#/)) {
42196                 return;
42197             }
42198 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42199             node.removeAttribute(n);
42200             
42201         }
42202         
42203         var cwhite = this.cwhite;
42204         var cblack = this.cblack;
42205             
42206         function cleanStyle(n,v)
42207         {
42208             if (v.match(/expression/)) { //XSS?? should we even bother..
42209                 node.removeAttribute(n);
42210                 return;
42211             }
42212             
42213             var parts = v.split(/;/);
42214             var clean = [];
42215             
42216             Roo.each(parts, function(p) {
42217                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42218                 if (!p.length) {
42219                     return true;
42220                 }
42221                 var l = p.split(':').shift().replace(/\s+/g,'');
42222                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42223                 
42224                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42225 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42226                     //node.removeAttribute(n);
42227                     return true;
42228                 }
42229                 //Roo.log()
42230                 // only allow 'c whitelisted system attributes'
42231                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42232 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42233                     //node.removeAttribute(n);
42234                     return true;
42235                 }
42236                 
42237                 
42238                  
42239                 
42240                 clean.push(p);
42241                 return true;
42242             });
42243             if (clean.length) { 
42244                 node.setAttribute(n, clean.join(';'));
42245             } else {
42246                 node.removeAttribute(n);
42247             }
42248             
42249         }
42250         
42251         
42252         for (var i = node.attributes.length-1; i > -1 ; i--) {
42253             var a = node.attributes[i];
42254             //console.log(a);
42255             
42256             if (a.name.toLowerCase().substr(0,2)=='on')  {
42257                 node.removeAttribute(a.name);
42258                 continue;
42259             }
42260             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42261                 node.removeAttribute(a.name);
42262                 continue;
42263             }
42264             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42265                 cleanAttr(a.name,a.value); // fixme..
42266                 continue;
42267             }
42268             if (a.name == 'style') {
42269                 cleanStyle(a.name,a.value);
42270                 continue;
42271             }
42272             /// clean up MS crap..
42273             // tecnically this should be a list of valid class'es..
42274             
42275             
42276             if (a.name == 'class') {
42277                 if (a.value.match(/^Mso/)) {
42278                     node.className = '';
42279                 }
42280                 
42281                 if (a.value.match(/body/)) {
42282                     node.className = '';
42283                 }
42284                 continue;
42285             }
42286             
42287             // style cleanup!?
42288             // class cleanup?
42289             
42290         }
42291         
42292         
42293         this.cleanUpChildren(node);
42294         
42295         
42296     },
42297     /**
42298      * Clean up MS wordisms...
42299      */
42300     cleanWord : function(node)
42301     {
42302         var _t = this;
42303         var cleanWordChildren = function()
42304         {
42305             if (!node.childNodes.length) {
42306                 return;
42307             }
42308             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42309                _t.cleanWord(node.childNodes[i]);
42310             }
42311         }
42312         
42313         
42314         if (!node) {
42315             this.cleanWord(this.doc.body);
42316             return;
42317         }
42318         if (node.nodeName == "#text") {
42319             // clean up silly Windows -- stuff?
42320             return; 
42321         }
42322         if (node.nodeName == "#comment") {
42323             node.parentNode.removeChild(node);
42324             // clean up silly Windows -- stuff?
42325             return; 
42326         }
42327         
42328         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42329             node.parentNode.removeChild(node);
42330             return;
42331         }
42332         
42333         // remove - but keep children..
42334         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42335             while (node.childNodes.length) {
42336                 var cn = node.childNodes[0];
42337                 node.removeChild(cn);
42338                 node.parentNode.insertBefore(cn, node);
42339             }
42340             node.parentNode.removeChild(node);
42341             cleanWordChildren();
42342             return;
42343         }
42344         // clean styles
42345         if (node.className.length) {
42346             
42347             var cn = node.className.split(/\W+/);
42348             var cna = [];
42349             Roo.each(cn, function(cls) {
42350                 if (cls.match(/Mso[a-zA-Z]+/)) {
42351                     return;
42352                 }
42353                 cna.push(cls);
42354             });
42355             node.className = cna.length ? cna.join(' ') : '';
42356             if (!cna.length) {
42357                 node.removeAttribute("class");
42358             }
42359         }
42360         
42361         if (node.hasAttribute("lang")) {
42362             node.removeAttribute("lang");
42363         }
42364         
42365         if (node.hasAttribute("style")) {
42366             
42367             var styles = node.getAttribute("style").split(";");
42368             var nstyle = [];
42369             Roo.each(styles, function(s) {
42370                 if (!s.match(/:/)) {
42371                     return;
42372                 }
42373                 var kv = s.split(":");
42374                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42375                     return;
42376                 }
42377                 // what ever is left... we allow.
42378                 nstyle.push(s);
42379             });
42380             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42381             if (!nstyle.length) {
42382                 node.removeAttribute('style');
42383             }
42384         }
42385         
42386         cleanWordChildren();
42387         
42388         
42389     },
42390     domToHTML : function(currentElement, depth, nopadtext) {
42391         
42392         depth = depth || 0;
42393         nopadtext = nopadtext || false;
42394     
42395         if (!currentElement) {
42396             return this.domToHTML(this.doc.body);
42397         }
42398         
42399         //Roo.log(currentElement);
42400         var j;
42401         var allText = false;
42402         var nodeName = currentElement.nodeName;
42403         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42404         
42405         if  (nodeName == '#text') {
42406             
42407             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42408         }
42409         
42410         
42411         var ret = '';
42412         if (nodeName != 'BODY') {
42413              
42414             var i = 0;
42415             // Prints the node tagName, such as <A>, <IMG>, etc
42416             if (tagName) {
42417                 var attr = [];
42418                 for(i = 0; i < currentElement.attributes.length;i++) {
42419                     // quoting?
42420                     var aname = currentElement.attributes.item(i).name;
42421                     if (!currentElement.attributes.item(i).value.length) {
42422                         continue;
42423                     }
42424                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42425                 }
42426                 
42427                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42428             } 
42429             else {
42430                 
42431                 // eack
42432             }
42433         } else {
42434             tagName = false;
42435         }
42436         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42437             return ret;
42438         }
42439         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42440             nopadtext = true;
42441         }
42442         
42443         
42444         // Traverse the tree
42445         i = 0;
42446         var currentElementChild = currentElement.childNodes.item(i);
42447         var allText = true;
42448         var innerHTML  = '';
42449         lastnode = '';
42450         while (currentElementChild) {
42451             // Formatting code (indent the tree so it looks nice on the screen)
42452             var nopad = nopadtext;
42453             if (lastnode == 'SPAN') {
42454                 nopad  = true;
42455             }
42456             // text
42457             if  (currentElementChild.nodeName == '#text') {
42458                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42459                 toadd = nopadtext ? toadd : toadd.trim();
42460                 if (!nopad && toadd.length > 80) {
42461                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42462                 }
42463                 innerHTML  += toadd;
42464                 
42465                 i++;
42466                 currentElementChild = currentElement.childNodes.item(i);
42467                 lastNode = '';
42468                 continue;
42469             }
42470             allText = false;
42471             
42472             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42473                 
42474             // Recursively traverse the tree structure of the child node
42475             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42476             lastnode = currentElementChild.nodeName;
42477             i++;
42478             currentElementChild=currentElement.childNodes.item(i);
42479         }
42480         
42481         ret += innerHTML;
42482         
42483         if (!allText) {
42484                 // The remaining code is mostly for formatting the tree
42485             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42486         }
42487         
42488         
42489         if (tagName) {
42490             ret+= "</"+tagName+">";
42491         }
42492         return ret;
42493         
42494     },
42495         
42496     applyBlacklists : function()
42497     {
42498         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42499         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42500         
42501         this.white = [];
42502         this.black = [];
42503         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42504             if (b.indexOf(tag) > -1) {
42505                 return;
42506             }
42507             this.white.push(tag);
42508             
42509         }, this);
42510         
42511         Roo.each(w, function(tag) {
42512             if (b.indexOf(tag) > -1) {
42513                 return;
42514             }
42515             if (this.white.indexOf(tag) > -1) {
42516                 return;
42517             }
42518             this.white.push(tag);
42519             
42520         }, this);
42521         
42522         
42523         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42524             if (w.indexOf(tag) > -1) {
42525                 return;
42526             }
42527             this.black.push(tag);
42528             
42529         }, this);
42530         
42531         Roo.each(b, function(tag) {
42532             if (w.indexOf(tag) > -1) {
42533                 return;
42534             }
42535             if (this.black.indexOf(tag) > -1) {
42536                 return;
42537             }
42538             this.black.push(tag);
42539             
42540         }, this);
42541         
42542         
42543         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42544         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42545         
42546         this.cwhite = [];
42547         this.cblack = [];
42548         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42549             if (b.indexOf(tag) > -1) {
42550                 return;
42551             }
42552             this.cwhite.push(tag);
42553             
42554         }, this);
42555         
42556         Roo.each(w, function(tag) {
42557             if (b.indexOf(tag) > -1) {
42558                 return;
42559             }
42560             if (this.cwhite.indexOf(tag) > -1) {
42561                 return;
42562             }
42563             this.cwhite.push(tag);
42564             
42565         }, this);
42566         
42567         
42568         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42569             if (w.indexOf(tag) > -1) {
42570                 return;
42571             }
42572             this.cblack.push(tag);
42573             
42574         }, this);
42575         
42576         Roo.each(b, function(tag) {
42577             if (w.indexOf(tag) > -1) {
42578                 return;
42579             }
42580             if (this.cblack.indexOf(tag) > -1) {
42581                 return;
42582             }
42583             this.cblack.push(tag);
42584             
42585         }, this);
42586     },
42587     
42588     setStylesheets : function(stylesheets)
42589     {
42590         if(typeof(stylesheets) == 'string'){
42591             Roo.get(this.iframe.contentDocument.head).createChild({
42592                 tag : 'link',
42593                 rel : 'stylesheet',
42594                 type : 'text/css',
42595                 href : stylesheets
42596             });
42597             
42598             return;
42599         }
42600         var _this = this;
42601      
42602         Roo.each(stylesheets, function(s) {
42603             if(!s.length){
42604                 return;
42605             }
42606             
42607             Roo.get(_this.iframe.contentDocument.head).createChild({
42608                 tag : 'link',
42609                 rel : 'stylesheet',
42610                 type : 'text/css',
42611                 href : s
42612             });
42613         });
42614
42615         
42616     },
42617     
42618     removeStylesheets : function()
42619     {
42620         var _this = this;
42621         
42622         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42623             s.remove();
42624         });
42625     }
42626     
42627     // hide stuff that is not compatible
42628     /**
42629      * @event blur
42630      * @hide
42631      */
42632     /**
42633      * @event change
42634      * @hide
42635      */
42636     /**
42637      * @event focus
42638      * @hide
42639      */
42640     /**
42641      * @event specialkey
42642      * @hide
42643      */
42644     /**
42645      * @cfg {String} fieldClass @hide
42646      */
42647     /**
42648      * @cfg {String} focusClass @hide
42649      */
42650     /**
42651      * @cfg {String} autoCreate @hide
42652      */
42653     /**
42654      * @cfg {String} inputType @hide
42655      */
42656     /**
42657      * @cfg {String} invalidClass @hide
42658      */
42659     /**
42660      * @cfg {String} invalidText @hide
42661      */
42662     /**
42663      * @cfg {String} msgFx @hide
42664      */
42665     /**
42666      * @cfg {String} validateOnBlur @hide
42667      */
42668 });
42669
42670 Roo.HtmlEditorCore.white = [
42671         'area', 'br', 'img', 'input', 'hr', 'wbr',
42672         
42673        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42674        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42675        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42676        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42677        'table',   'ul',         'xmp', 
42678        
42679        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42680       'thead',   'tr', 
42681      
42682       'dir', 'menu', 'ol', 'ul', 'dl',
42683        
42684       'embed',  'object'
42685 ];
42686
42687
42688 Roo.HtmlEditorCore.black = [
42689     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42690         'applet', // 
42691         'base',   'basefont', 'bgsound', 'blink',  'body', 
42692         'frame',  'frameset', 'head',    'html',   'ilayer', 
42693         'iframe', 'layer',  'link',     'meta',    'object',   
42694         'script', 'style' ,'title',  'xml' // clean later..
42695 ];
42696 Roo.HtmlEditorCore.clean = [
42697     'script', 'style', 'title', 'xml'
42698 ];
42699 Roo.HtmlEditorCore.remove = [
42700     'font'
42701 ];
42702 // attributes..
42703
42704 Roo.HtmlEditorCore.ablack = [
42705     'on'
42706 ];
42707     
42708 Roo.HtmlEditorCore.aclean = [ 
42709     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42710 ];
42711
42712 // protocols..
42713 Roo.HtmlEditorCore.pwhite= [
42714         'http',  'https',  'mailto'
42715 ];
42716
42717 // white listed style attributes.
42718 Roo.HtmlEditorCore.cwhite= [
42719       //  'text-align', /// default is to allow most things..
42720       
42721          
42722 //        'font-size'//??
42723 ];
42724
42725 // black listed style attributes.
42726 Roo.HtmlEditorCore.cblack= [
42727       //  'font-size' -- this can be set by the project 
42728 ];
42729
42730
42731 Roo.HtmlEditorCore.swapCodes   =[ 
42732     [    8211, "--" ], 
42733     [    8212, "--" ], 
42734     [    8216,  "'" ],  
42735     [    8217, "'" ],  
42736     [    8220, '"' ],  
42737     [    8221, '"' ],  
42738     [    8226, "*" ],  
42739     [    8230, "..." ]
42740 ]; 
42741
42742     //<script type="text/javascript">
42743
42744 /*
42745  * Ext JS Library 1.1.1
42746  * Copyright(c) 2006-2007, Ext JS, LLC.
42747  * Licence LGPL
42748  * 
42749  */
42750  
42751  
42752 Roo.form.HtmlEditor = function(config){
42753     
42754     
42755     
42756     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42757     
42758     if (!this.toolbars) {
42759         this.toolbars = [];
42760     }
42761     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42762     
42763     
42764 };
42765
42766 /**
42767  * @class Roo.form.HtmlEditor
42768  * @extends Roo.form.Field
42769  * Provides a lightweight HTML Editor component.
42770  *
42771  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42772  * 
42773  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42774  * supported by this editor.</b><br/><br/>
42775  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42776  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42777  */
42778 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42779     /**
42780      * @cfg {Boolean} clearUp
42781      */
42782     clearUp : true,
42783       /**
42784      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42785      */
42786     toolbars : false,
42787    
42788      /**
42789      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42790      *                        Roo.resizable.
42791      */
42792     resizable : false,
42793      /**
42794      * @cfg {Number} height (in pixels)
42795      */   
42796     height: 300,
42797    /**
42798      * @cfg {Number} width (in pixels)
42799      */   
42800     width: 500,
42801     
42802     /**
42803      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42804      * 
42805      */
42806     stylesheets: false,
42807     
42808     
42809      /**
42810      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42811      * 
42812      */
42813     cblack: false,
42814     /**
42815      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42816      * 
42817      */
42818     cwhite: false,
42819     
42820      /**
42821      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42822      * 
42823      */
42824     black: false,
42825     /**
42826      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42827      * 
42828      */
42829     white: false,
42830     
42831     // id of frame..
42832     frameId: false,
42833     
42834     // private properties
42835     validationEvent : false,
42836     deferHeight: true,
42837     initialized : false,
42838     activated : false,
42839     
42840     onFocus : Roo.emptyFn,
42841     iframePad:3,
42842     hideMode:'offsets',
42843     
42844     actionMode : 'container', // defaults to hiding it...
42845     
42846     defaultAutoCreate : { // modified by initCompnoent..
42847         tag: "textarea",
42848         style:"width:500px;height:300px;",
42849         autocomplete: "new-password"
42850     },
42851
42852     // private
42853     initComponent : function(){
42854         this.addEvents({
42855             /**
42856              * @event initialize
42857              * Fires when the editor is fully initialized (including the iframe)
42858              * @param {HtmlEditor} this
42859              */
42860             initialize: true,
42861             /**
42862              * @event activate
42863              * Fires when the editor is first receives the focus. Any insertion must wait
42864              * until after this event.
42865              * @param {HtmlEditor} this
42866              */
42867             activate: true,
42868              /**
42869              * @event beforesync
42870              * Fires before the textarea is updated with content from the editor iframe. Return false
42871              * to cancel the sync.
42872              * @param {HtmlEditor} this
42873              * @param {String} html
42874              */
42875             beforesync: true,
42876              /**
42877              * @event beforepush
42878              * Fires before the iframe editor is updated with content from the textarea. Return false
42879              * to cancel the push.
42880              * @param {HtmlEditor} this
42881              * @param {String} html
42882              */
42883             beforepush: true,
42884              /**
42885              * @event sync
42886              * Fires when the textarea is updated with content from the editor iframe.
42887              * @param {HtmlEditor} this
42888              * @param {String} html
42889              */
42890             sync: true,
42891              /**
42892              * @event push
42893              * Fires when the iframe editor is updated with content from the textarea.
42894              * @param {HtmlEditor} this
42895              * @param {String} html
42896              */
42897             push: true,
42898              /**
42899              * @event editmodechange
42900              * Fires when the editor switches edit modes
42901              * @param {HtmlEditor} this
42902              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42903              */
42904             editmodechange: true,
42905             /**
42906              * @event editorevent
42907              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42908              * @param {HtmlEditor} this
42909              */
42910             editorevent: true,
42911             /**
42912              * @event firstfocus
42913              * Fires when on first focus - needed by toolbars..
42914              * @param {HtmlEditor} this
42915              */
42916             firstfocus: true,
42917             /**
42918              * @event autosave
42919              * Auto save the htmlEditor value as a file into Events
42920              * @param {HtmlEditor} this
42921              */
42922             autosave: true,
42923             /**
42924              * @event savedpreview
42925              * preview the saved version of htmlEditor
42926              * @param {HtmlEditor} this
42927              */
42928             savedpreview: true,
42929             
42930             /**
42931             * @event stylesheetsclick
42932             * Fires when press the Sytlesheets button
42933             * @param {Roo.HtmlEditorCore} this
42934             */
42935             stylesheetsclick: true
42936         });
42937         this.defaultAutoCreate =  {
42938             tag: "textarea",
42939             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42940             autocomplete: "new-password"
42941         };
42942     },
42943
42944     /**
42945      * Protected method that will not generally be called directly. It
42946      * is called when the editor creates its toolbar. Override this method if you need to
42947      * add custom toolbar buttons.
42948      * @param {HtmlEditor} editor
42949      */
42950     createToolbar : function(editor){
42951         Roo.log("create toolbars");
42952         if (!editor.toolbars || !editor.toolbars.length) {
42953             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42954         }
42955         
42956         for (var i =0 ; i < editor.toolbars.length;i++) {
42957             editor.toolbars[i] = Roo.factory(
42958                     typeof(editor.toolbars[i]) == 'string' ?
42959                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42960                 Roo.form.HtmlEditor);
42961             editor.toolbars[i].init(editor);
42962         }
42963          
42964         
42965     },
42966
42967      
42968     // private
42969     onRender : function(ct, position)
42970     {
42971         var _t = this;
42972         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42973         
42974         this.wrap = this.el.wrap({
42975             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42976         });
42977         
42978         this.editorcore.onRender(ct, position);
42979          
42980         if (this.resizable) {
42981             this.resizeEl = new Roo.Resizable(this.wrap, {
42982                 pinned : true,
42983                 wrap: true,
42984                 dynamic : true,
42985                 minHeight : this.height,
42986                 height: this.height,
42987                 handles : this.resizable,
42988                 width: this.width,
42989                 listeners : {
42990                     resize : function(r, w, h) {
42991                         _t.onResize(w,h); // -something
42992                     }
42993                 }
42994             });
42995             
42996         }
42997         this.createToolbar(this);
42998        
42999         
43000         if(!this.width){
43001             this.setSize(this.wrap.getSize());
43002         }
43003         if (this.resizeEl) {
43004             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43005             // should trigger onReize..
43006         }
43007         
43008         this.keyNav = new Roo.KeyNav(this.el, {
43009             
43010             "tab" : function(e){
43011                 e.preventDefault();
43012                 
43013                 var value = this.getValue();
43014                 
43015                 var start = this.el.dom.selectionStart;
43016                 var end = this.el.dom.selectionEnd;
43017                 
43018                 if(!e.shiftKey){
43019                     
43020                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43021                     this.el.dom.setSelectionRange(end + 1, end + 1);
43022                     return;
43023                 }
43024                 
43025                 var f = value.substring(0, start).split("\t");
43026                 
43027                 if(f.pop().length != 0){
43028                     return;
43029                 }
43030                 
43031                 this.setValue(f.join("\t") + value.substring(end));
43032                 this.el.dom.setSelectionRange(start - 1, start - 1);
43033                 
43034             },
43035             
43036             "home" : function(e){
43037                 e.preventDefault();
43038                 
43039                 var curr = this.el.dom.selectionStart;
43040                 var lines = this.getValue().split("\n");
43041                 
43042                 if(!lines.length){
43043                     return;
43044                 }
43045                 
43046                 if(e.ctrlKey){
43047                     this.el.dom.setSelectionRange(0, 0);
43048                     return;
43049                 }
43050                 
43051                 var pos = 0;
43052                 
43053                 for (var i = 0; i < lines.length;i++) {
43054                     pos += lines[i].length;
43055                     
43056                     if(i != 0){
43057                         pos += 1;
43058                     }
43059                     
43060                     if(pos < curr){
43061                         continue;
43062                     }
43063                     
43064                     pos -= lines[i].length;
43065                     
43066                     break;
43067                 }
43068                 
43069                 if(!e.shiftKey){
43070                     this.el.dom.setSelectionRange(pos, pos);
43071                     return;
43072                 }
43073                 
43074                 this.el.dom.selectionStart = pos;
43075                 this.el.dom.selectionEnd = curr;
43076             },
43077             
43078             "end" : function(e){
43079                 e.preventDefault();
43080                 
43081                 var curr = this.el.dom.selectionStart;
43082                 var lines = this.getValue().split("\n");
43083                 
43084                 if(!lines.length){
43085                     return;
43086                 }
43087                 
43088                 if(e.ctrlKey){
43089                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43090                     return;
43091                 }
43092                 
43093                 var pos = 0;
43094                 
43095                 for (var i = 0; i < lines.length;i++) {
43096                     
43097                     pos += lines[i].length;
43098                     
43099                     if(i != 0){
43100                         pos += 1;
43101                     }
43102                     
43103                     if(pos < curr){
43104                         continue;
43105                     }
43106                     
43107                     break;
43108                 }
43109                 
43110                 if(!e.shiftKey){
43111                     this.el.dom.setSelectionRange(pos, pos);
43112                     return;
43113                 }
43114                 
43115                 this.el.dom.selectionStart = curr;
43116                 this.el.dom.selectionEnd = pos;
43117             },
43118
43119             scope : this,
43120
43121             doRelay : function(foo, bar, hname){
43122                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43123             },
43124
43125             forceKeyDown: true
43126         });
43127         
43128 //        if(this.autosave && this.w){
43129 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43130 //        }
43131     },
43132
43133     // private
43134     onResize : function(w, h)
43135     {
43136         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43137         var ew = false;
43138         var eh = false;
43139         
43140         if(this.el ){
43141             if(typeof w == 'number'){
43142                 var aw = w - this.wrap.getFrameWidth('lr');
43143                 this.el.setWidth(this.adjustWidth('textarea', aw));
43144                 ew = aw;
43145             }
43146             if(typeof h == 'number'){
43147                 var tbh = 0;
43148                 for (var i =0; i < this.toolbars.length;i++) {
43149                     // fixme - ask toolbars for heights?
43150                     tbh += this.toolbars[i].tb.el.getHeight();
43151                     if (this.toolbars[i].footer) {
43152                         tbh += this.toolbars[i].footer.el.getHeight();
43153                     }
43154                 }
43155                 
43156                 
43157                 
43158                 
43159                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43160                 ah -= 5; // knock a few pixes off for look..
43161 //                Roo.log(ah);
43162                 this.el.setHeight(this.adjustWidth('textarea', ah));
43163                 var eh = ah;
43164             }
43165         }
43166         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43167         this.editorcore.onResize(ew,eh);
43168         
43169     },
43170
43171     /**
43172      * Toggles the editor between standard and source edit mode.
43173      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43174      */
43175     toggleSourceEdit : function(sourceEditMode)
43176     {
43177         this.editorcore.toggleSourceEdit(sourceEditMode);
43178         
43179         if(this.editorcore.sourceEditMode){
43180             Roo.log('editor - showing textarea');
43181             
43182 //            Roo.log('in');
43183 //            Roo.log(this.syncValue());
43184             this.editorcore.syncValue();
43185             this.el.removeClass('x-hidden');
43186             this.el.dom.removeAttribute('tabIndex');
43187             this.el.focus();
43188             
43189             for (var i = 0; i < this.toolbars.length; i++) {
43190                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43191                     this.toolbars[i].tb.hide();
43192                     this.toolbars[i].footer.hide();
43193                 }
43194             }
43195             
43196         }else{
43197             Roo.log('editor - hiding textarea');
43198 //            Roo.log('out')
43199 //            Roo.log(this.pushValue()); 
43200             this.editorcore.pushValue();
43201             
43202             this.el.addClass('x-hidden');
43203             this.el.dom.setAttribute('tabIndex', -1);
43204             
43205             for (var i = 0; i < this.toolbars.length; i++) {
43206                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43207                     this.toolbars[i].tb.show();
43208                     this.toolbars[i].footer.show();
43209                 }
43210             }
43211             
43212             //this.deferFocus();
43213         }
43214         
43215         this.setSize(this.wrap.getSize());
43216         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43217         
43218         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43219     },
43220  
43221     // private (for BoxComponent)
43222     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43223
43224     // private (for BoxComponent)
43225     getResizeEl : function(){
43226         return this.wrap;
43227     },
43228
43229     // private (for BoxComponent)
43230     getPositionEl : function(){
43231         return this.wrap;
43232     },
43233
43234     // private
43235     initEvents : function(){
43236         this.originalValue = this.getValue();
43237     },
43238
43239     /**
43240      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43241      * @method
43242      */
43243     markInvalid : Roo.emptyFn,
43244     /**
43245      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43246      * @method
43247      */
43248     clearInvalid : Roo.emptyFn,
43249
43250     setValue : function(v){
43251         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43252         this.editorcore.pushValue();
43253     },
43254
43255      
43256     // private
43257     deferFocus : function(){
43258         this.focus.defer(10, this);
43259     },
43260
43261     // doc'ed in Field
43262     focus : function(){
43263         this.editorcore.focus();
43264         
43265     },
43266       
43267
43268     // private
43269     onDestroy : function(){
43270         
43271         
43272         
43273         if(this.rendered){
43274             
43275             for (var i =0; i < this.toolbars.length;i++) {
43276                 // fixme - ask toolbars for heights?
43277                 this.toolbars[i].onDestroy();
43278             }
43279             
43280             this.wrap.dom.innerHTML = '';
43281             this.wrap.remove();
43282         }
43283     },
43284
43285     // private
43286     onFirstFocus : function(){
43287         //Roo.log("onFirstFocus");
43288         this.editorcore.onFirstFocus();
43289          for (var i =0; i < this.toolbars.length;i++) {
43290             this.toolbars[i].onFirstFocus();
43291         }
43292         
43293     },
43294     
43295     // private
43296     syncValue : function()
43297     {
43298         this.editorcore.syncValue();
43299     },
43300     
43301     pushValue : function()
43302     {
43303         this.editorcore.pushValue();
43304     },
43305     
43306     setStylesheets : function(stylesheets)
43307     {
43308         this.editorcore.setStylesheets(stylesheets);
43309     },
43310     
43311     removeStylesheets : function()
43312     {
43313         this.editorcore.removeStylesheets();
43314     }
43315      
43316     
43317     // hide stuff that is not compatible
43318     /**
43319      * @event blur
43320      * @hide
43321      */
43322     /**
43323      * @event change
43324      * @hide
43325      */
43326     /**
43327      * @event focus
43328      * @hide
43329      */
43330     /**
43331      * @event specialkey
43332      * @hide
43333      */
43334     /**
43335      * @cfg {String} fieldClass @hide
43336      */
43337     /**
43338      * @cfg {String} focusClass @hide
43339      */
43340     /**
43341      * @cfg {String} autoCreate @hide
43342      */
43343     /**
43344      * @cfg {String} inputType @hide
43345      */
43346     /**
43347      * @cfg {String} invalidClass @hide
43348      */
43349     /**
43350      * @cfg {String} invalidText @hide
43351      */
43352     /**
43353      * @cfg {String} msgFx @hide
43354      */
43355     /**
43356      * @cfg {String} validateOnBlur @hide
43357      */
43358 });
43359  
43360     // <script type="text/javascript">
43361 /*
43362  * Based on
43363  * Ext JS Library 1.1.1
43364  * Copyright(c) 2006-2007, Ext JS, LLC.
43365  *  
43366  
43367  */
43368
43369 /**
43370  * @class Roo.form.HtmlEditorToolbar1
43371  * Basic Toolbar
43372  * 
43373  * Usage:
43374  *
43375  new Roo.form.HtmlEditor({
43376     ....
43377     toolbars : [
43378         new Roo.form.HtmlEditorToolbar1({
43379             disable : { fonts: 1 , format: 1, ..., ... , ...],
43380             btns : [ .... ]
43381         })
43382     }
43383      
43384  * 
43385  * @cfg {Object} disable List of elements to disable..
43386  * @cfg {Array} btns List of additional buttons.
43387  * 
43388  * 
43389  * NEEDS Extra CSS? 
43390  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43391  */
43392  
43393 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43394 {
43395     
43396     Roo.apply(this, config);
43397     
43398     // default disabled, based on 'good practice'..
43399     this.disable = this.disable || {};
43400     Roo.applyIf(this.disable, {
43401         fontSize : true,
43402         colors : true,
43403         specialElements : true
43404     });
43405     
43406     
43407     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43408     // dont call parent... till later.
43409 }
43410
43411 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43412     
43413     tb: false,
43414     
43415     rendered: false,
43416     
43417     editor : false,
43418     editorcore : false,
43419     /**
43420      * @cfg {Object} disable  List of toolbar elements to disable
43421          
43422      */
43423     disable : false,
43424     
43425     
43426      /**
43427      * @cfg {String} createLinkText The default text for the create link prompt
43428      */
43429     createLinkText : 'Please enter the URL for the link:',
43430     /**
43431      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43432      */
43433     defaultLinkValue : 'http:/'+'/',
43434    
43435     
43436       /**
43437      * @cfg {Array} fontFamilies An array of available font families
43438      */
43439     fontFamilies : [
43440         'Arial',
43441         'Courier New',
43442         'Tahoma',
43443         'Times New Roman',
43444         'Verdana'
43445     ],
43446     
43447     specialChars : [
43448            "&#169;",
43449           "&#174;",     
43450           "&#8482;",    
43451           "&#163;" ,    
43452          // "&#8212;",    
43453           "&#8230;",    
43454           "&#247;" ,    
43455         //  "&#225;" ,     ?? a acute?
43456            "&#8364;"    , //Euro
43457        //   "&#8220;"    ,
43458         //  "&#8221;"    ,
43459         //  "&#8226;"    ,
43460           "&#176;"  //   , // degrees
43461
43462          // "&#233;"     , // e ecute
43463          // "&#250;"     , // u ecute?
43464     ],
43465     
43466     specialElements : [
43467         {
43468             text: "Insert Table",
43469             xtype: 'MenuItem',
43470             xns : Roo.Menu,
43471             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43472                 
43473         },
43474         {    
43475             text: "Insert Image",
43476             xtype: 'MenuItem',
43477             xns : Roo.Menu,
43478             ihtml : '<img src="about:blank"/>'
43479             
43480         }
43481         
43482          
43483     ],
43484     
43485     
43486     inputElements : [ 
43487             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43488             "input:submit", "input:button", "select", "textarea", "label" ],
43489     formats : [
43490         ["p"] ,  
43491         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43492         ["pre"],[ "code"], 
43493         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43494         ['div'],['span']
43495     ],
43496     
43497     cleanStyles : [
43498         "font-size"
43499     ],
43500      /**
43501      * @cfg {String} defaultFont default font to use.
43502      */
43503     defaultFont: 'tahoma',
43504    
43505     fontSelect : false,
43506     
43507     
43508     formatCombo : false,
43509     
43510     init : function(editor)
43511     {
43512         this.editor = editor;
43513         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43514         var editorcore = this.editorcore;
43515         
43516         var _t = this;
43517         
43518         var fid = editorcore.frameId;
43519         var etb = this;
43520         function btn(id, toggle, handler){
43521             var xid = fid + '-'+ id ;
43522             return {
43523                 id : xid,
43524                 cmd : id,
43525                 cls : 'x-btn-icon x-edit-'+id,
43526                 enableToggle:toggle !== false,
43527                 scope: _t, // was editor...
43528                 handler:handler||_t.relayBtnCmd,
43529                 clickEvent:'mousedown',
43530                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43531                 tabIndex:-1
43532             };
43533         }
43534         
43535         
43536         
43537         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43538         this.tb = tb;
43539          // stop form submits
43540         tb.el.on('click', function(e){
43541             e.preventDefault(); // what does this do?
43542         });
43543
43544         if(!this.disable.font) { // && !Roo.isSafari){
43545             /* why no safari for fonts 
43546             editor.fontSelect = tb.el.createChild({
43547                 tag:'select',
43548                 tabIndex: -1,
43549                 cls:'x-font-select',
43550                 html: this.createFontOptions()
43551             });
43552             
43553             editor.fontSelect.on('change', function(){
43554                 var font = editor.fontSelect.dom.value;
43555                 editor.relayCmd('fontname', font);
43556                 editor.deferFocus();
43557             }, editor);
43558             
43559             tb.add(
43560                 editor.fontSelect.dom,
43561                 '-'
43562             );
43563             */
43564             
43565         };
43566         if(!this.disable.formats){
43567             this.formatCombo = new Roo.form.ComboBox({
43568                 store: new Roo.data.SimpleStore({
43569                     id : 'tag',
43570                     fields: ['tag'],
43571                     data : this.formats // from states.js
43572                 }),
43573                 blockFocus : true,
43574                 name : '',
43575                 //autoCreate : {tag: "div",  size: "20"},
43576                 displayField:'tag',
43577                 typeAhead: false,
43578                 mode: 'local',
43579                 editable : false,
43580                 triggerAction: 'all',
43581                 emptyText:'Add tag',
43582                 selectOnFocus:true,
43583                 width:135,
43584                 listeners : {
43585                     'select': function(c, r, i) {
43586                         editorcore.insertTag(r.get('tag'));
43587                         editor.focus();
43588                     }
43589                 }
43590
43591             });
43592             tb.addField(this.formatCombo);
43593             
43594         }
43595         
43596         if(!this.disable.format){
43597             tb.add(
43598                 btn('bold'),
43599                 btn('italic'),
43600                 btn('underline')
43601             );
43602         };
43603         if(!this.disable.fontSize){
43604             tb.add(
43605                 '-',
43606                 
43607                 
43608                 btn('increasefontsize', false, editorcore.adjustFont),
43609                 btn('decreasefontsize', false, editorcore.adjustFont)
43610             );
43611         };
43612         
43613         
43614         if(!this.disable.colors){
43615             tb.add(
43616                 '-', {
43617                     id:editorcore.frameId +'-forecolor',
43618                     cls:'x-btn-icon x-edit-forecolor',
43619                     clickEvent:'mousedown',
43620                     tooltip: this.buttonTips['forecolor'] || undefined,
43621                     tabIndex:-1,
43622                     menu : new Roo.menu.ColorMenu({
43623                         allowReselect: true,
43624                         focus: Roo.emptyFn,
43625                         value:'000000',
43626                         plain:true,
43627                         selectHandler: function(cp, color){
43628                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43629                             editor.deferFocus();
43630                         },
43631                         scope: editorcore,
43632                         clickEvent:'mousedown'
43633                     })
43634                 }, {
43635                     id:editorcore.frameId +'backcolor',
43636                     cls:'x-btn-icon x-edit-backcolor',
43637                     clickEvent:'mousedown',
43638                     tooltip: this.buttonTips['backcolor'] || undefined,
43639                     tabIndex:-1,
43640                     menu : new Roo.menu.ColorMenu({
43641                         focus: Roo.emptyFn,
43642                         value:'FFFFFF',
43643                         plain:true,
43644                         allowReselect: true,
43645                         selectHandler: function(cp, color){
43646                             if(Roo.isGecko){
43647                                 editorcore.execCmd('useCSS', false);
43648                                 editorcore.execCmd('hilitecolor', color);
43649                                 editorcore.execCmd('useCSS', true);
43650                                 editor.deferFocus();
43651                             }else{
43652                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43653                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43654                                 editor.deferFocus();
43655                             }
43656                         },
43657                         scope:editorcore,
43658                         clickEvent:'mousedown'
43659                     })
43660                 }
43661             );
43662         };
43663         // now add all the items...
43664         
43665
43666         if(!this.disable.alignments){
43667             tb.add(
43668                 '-',
43669                 btn('justifyleft'),
43670                 btn('justifycenter'),
43671                 btn('justifyright')
43672             );
43673         };
43674
43675         //if(!Roo.isSafari){
43676             if(!this.disable.links){
43677                 tb.add(
43678                     '-',
43679                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43680                 );
43681             };
43682
43683             if(!this.disable.lists){
43684                 tb.add(
43685                     '-',
43686                     btn('insertorderedlist'),
43687                     btn('insertunorderedlist')
43688                 );
43689             }
43690             if(!this.disable.sourceEdit){
43691                 tb.add(
43692                     '-',
43693                     btn('sourceedit', true, function(btn){
43694                         this.toggleSourceEdit(btn.pressed);
43695                     })
43696                 );
43697             }
43698         //}
43699         
43700         var smenu = { };
43701         // special menu.. - needs to be tidied up..
43702         if (!this.disable.special) {
43703             smenu = {
43704                 text: "&#169;",
43705                 cls: 'x-edit-none',
43706                 
43707                 menu : {
43708                     items : []
43709                 }
43710             };
43711             for (var i =0; i < this.specialChars.length; i++) {
43712                 smenu.menu.items.push({
43713                     
43714                     html: this.specialChars[i],
43715                     handler: function(a,b) {
43716                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43717                         //editor.insertAtCursor(a.html);
43718                         
43719                     },
43720                     tabIndex:-1
43721                 });
43722             }
43723             
43724             
43725             tb.add(smenu);
43726             
43727             
43728         }
43729         
43730         var cmenu = { };
43731         if (!this.disable.cleanStyles) {
43732             cmenu = {
43733                 cls: 'x-btn-icon x-btn-clear',
43734                 
43735                 menu : {
43736                     items : []
43737                 }
43738             };
43739             for (var i =0; i < this.cleanStyles.length; i++) {
43740                 cmenu.menu.items.push({
43741                     actiontype : this.cleanStyles[i],
43742                     html: 'Remove ' + this.cleanStyles[i],
43743                     handler: function(a,b) {
43744 //                        Roo.log(a);
43745 //                        Roo.log(b);
43746                         var c = Roo.get(editorcore.doc.body);
43747                         c.select('[style]').each(function(s) {
43748                             s.dom.style.removeProperty(a.actiontype);
43749                         });
43750                         editorcore.syncValue();
43751                     },
43752                     tabIndex:-1
43753                 });
43754             }
43755             cmenu.menu.items.push({
43756                 actiontype : 'word',
43757                 html: 'Remove MS Word Formating',
43758                 handler: function(a,b) {
43759                     editorcore.cleanWord();
43760                     editorcore.syncValue();
43761                 },
43762                 tabIndex:-1
43763             });
43764             
43765             cmenu.menu.items.push({
43766                 actiontype : 'all',
43767                 html: 'Remove All Styles',
43768                 handler: function(a,b) {
43769                     
43770                     var c = Roo.get(editorcore.doc.body);
43771                     c.select('[style]').each(function(s) {
43772                         s.dom.removeAttribute('style');
43773                     });
43774                     editorcore.syncValue();
43775                 },
43776                 tabIndex:-1
43777             });
43778              cmenu.menu.items.push({
43779                 actiontype : 'word',
43780                 html: 'Tidy HTML Source',
43781                 handler: function(a,b) {
43782                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43783                     editorcore.syncValue();
43784                 },
43785                 tabIndex:-1
43786             });
43787             
43788             
43789             tb.add(cmenu);
43790         }
43791          
43792         if (!this.disable.specialElements) {
43793             var semenu = {
43794                 text: "Other;",
43795                 cls: 'x-edit-none',
43796                 menu : {
43797                     items : []
43798                 }
43799             };
43800             for (var i =0; i < this.specialElements.length; i++) {
43801                 semenu.menu.items.push(
43802                     Roo.apply({ 
43803                         handler: function(a,b) {
43804                             editor.insertAtCursor(this.ihtml);
43805                         }
43806                     }, this.specialElements[i])
43807                 );
43808                     
43809             }
43810             
43811             tb.add(semenu);
43812             
43813             
43814         }
43815          
43816         
43817         if (this.btns) {
43818             for(var i =0; i< this.btns.length;i++) {
43819                 var b = Roo.factory(this.btns[i],Roo.form);
43820                 b.cls =  'x-edit-none';
43821                 
43822                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43823                     b.cls += ' x-init-enable';
43824                 }
43825                 
43826                 b.scope = editorcore;
43827                 tb.add(b);
43828             }
43829         
43830         }
43831         
43832         
43833         
43834         // disable everything...
43835         
43836         this.tb.items.each(function(item){
43837             
43838            if(
43839                 item.id != editorcore.frameId+ '-sourceedit' && 
43840                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43841             ){
43842                 
43843                 item.disable();
43844             }
43845         });
43846         this.rendered = true;
43847         
43848         // the all the btns;
43849         editor.on('editorevent', this.updateToolbar, this);
43850         // other toolbars need to implement this..
43851         //editor.on('editmodechange', this.updateToolbar, this);
43852     },
43853     
43854     
43855     relayBtnCmd : function(btn) {
43856         this.editorcore.relayCmd(btn.cmd);
43857     },
43858     // private used internally
43859     createLink : function(){
43860         Roo.log("create link?");
43861         var url = prompt(this.createLinkText, this.defaultLinkValue);
43862         if(url && url != 'http:/'+'/'){
43863             this.editorcore.relayCmd('createlink', url);
43864         }
43865     },
43866
43867     
43868     /**
43869      * Protected method that will not generally be called directly. It triggers
43870      * a toolbar update by reading the markup state of the current selection in the editor.
43871      */
43872     updateToolbar: function(){
43873
43874         if(!this.editorcore.activated){
43875             this.editor.onFirstFocus();
43876             return;
43877         }
43878
43879         var btns = this.tb.items.map, 
43880             doc = this.editorcore.doc,
43881             frameId = this.editorcore.frameId;
43882
43883         if(!this.disable.font && !Roo.isSafari){
43884             /*
43885             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43886             if(name != this.fontSelect.dom.value){
43887                 this.fontSelect.dom.value = name;
43888             }
43889             */
43890         }
43891         if(!this.disable.format){
43892             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43893             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43894             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43895         }
43896         if(!this.disable.alignments){
43897             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43898             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43899             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43900         }
43901         if(!Roo.isSafari && !this.disable.lists){
43902             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43903             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43904         }
43905         
43906         var ans = this.editorcore.getAllAncestors();
43907         if (this.formatCombo) {
43908             
43909             
43910             var store = this.formatCombo.store;
43911             this.formatCombo.setValue("");
43912             for (var i =0; i < ans.length;i++) {
43913                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43914                     // select it..
43915                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43916                     break;
43917                 }
43918             }
43919         }
43920         
43921         
43922         
43923         // hides menus... - so this cant be on a menu...
43924         Roo.menu.MenuMgr.hideAll();
43925
43926         //this.editorsyncValue();
43927     },
43928    
43929     
43930     createFontOptions : function(){
43931         var buf = [], fs = this.fontFamilies, ff, lc;
43932         
43933         
43934         
43935         for(var i = 0, len = fs.length; i< len; i++){
43936             ff = fs[i];
43937             lc = ff.toLowerCase();
43938             buf.push(
43939                 '<option value="',lc,'" style="font-family:',ff,';"',
43940                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43941                     ff,
43942                 '</option>'
43943             );
43944         }
43945         return buf.join('');
43946     },
43947     
43948     toggleSourceEdit : function(sourceEditMode){
43949         
43950         Roo.log("toolbar toogle");
43951         if(sourceEditMode === undefined){
43952             sourceEditMode = !this.sourceEditMode;
43953         }
43954         this.sourceEditMode = sourceEditMode === true;
43955         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43956         // just toggle the button?
43957         if(btn.pressed !== this.sourceEditMode){
43958             btn.toggle(this.sourceEditMode);
43959             return;
43960         }
43961         
43962         if(sourceEditMode){
43963             Roo.log("disabling buttons");
43964             this.tb.items.each(function(item){
43965                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43966                     item.disable();
43967                 }
43968             });
43969           
43970         }else{
43971             Roo.log("enabling buttons");
43972             if(this.editorcore.initialized){
43973                 this.tb.items.each(function(item){
43974                     item.enable();
43975                 });
43976             }
43977             
43978         }
43979         Roo.log("calling toggole on editor");
43980         // tell the editor that it's been pressed..
43981         this.editor.toggleSourceEdit(sourceEditMode);
43982        
43983     },
43984      /**
43985      * Object collection of toolbar tooltips for the buttons in the editor. The key
43986      * is the command id associated with that button and the value is a valid QuickTips object.
43987      * For example:
43988 <pre><code>
43989 {
43990     bold : {
43991         title: 'Bold (Ctrl+B)',
43992         text: 'Make the selected text bold.',
43993         cls: 'x-html-editor-tip'
43994     },
43995     italic : {
43996         title: 'Italic (Ctrl+I)',
43997         text: 'Make the selected text italic.',
43998         cls: 'x-html-editor-tip'
43999     },
44000     ...
44001 </code></pre>
44002     * @type Object
44003      */
44004     buttonTips : {
44005         bold : {
44006             title: 'Bold (Ctrl+B)',
44007             text: 'Make the selected text bold.',
44008             cls: 'x-html-editor-tip'
44009         },
44010         italic : {
44011             title: 'Italic (Ctrl+I)',
44012             text: 'Make the selected text italic.',
44013             cls: 'x-html-editor-tip'
44014         },
44015         underline : {
44016             title: 'Underline (Ctrl+U)',
44017             text: 'Underline the selected text.',
44018             cls: 'x-html-editor-tip'
44019         },
44020         increasefontsize : {
44021             title: 'Grow Text',
44022             text: 'Increase the font size.',
44023             cls: 'x-html-editor-tip'
44024         },
44025         decreasefontsize : {
44026             title: 'Shrink Text',
44027             text: 'Decrease the font size.',
44028             cls: 'x-html-editor-tip'
44029         },
44030         backcolor : {
44031             title: 'Text Highlight Color',
44032             text: 'Change the background color of the selected text.',
44033             cls: 'x-html-editor-tip'
44034         },
44035         forecolor : {
44036             title: 'Font Color',
44037             text: 'Change the color of the selected text.',
44038             cls: 'x-html-editor-tip'
44039         },
44040         justifyleft : {
44041             title: 'Align Text Left',
44042             text: 'Align text to the left.',
44043             cls: 'x-html-editor-tip'
44044         },
44045         justifycenter : {
44046             title: 'Center Text',
44047             text: 'Center text in the editor.',
44048             cls: 'x-html-editor-tip'
44049         },
44050         justifyright : {
44051             title: 'Align Text Right',
44052             text: 'Align text to the right.',
44053             cls: 'x-html-editor-tip'
44054         },
44055         insertunorderedlist : {
44056             title: 'Bullet List',
44057             text: 'Start a bulleted list.',
44058             cls: 'x-html-editor-tip'
44059         },
44060         insertorderedlist : {
44061             title: 'Numbered List',
44062             text: 'Start a numbered list.',
44063             cls: 'x-html-editor-tip'
44064         },
44065         createlink : {
44066             title: 'Hyperlink',
44067             text: 'Make the selected text a hyperlink.',
44068             cls: 'x-html-editor-tip'
44069         },
44070         sourceedit : {
44071             title: 'Source Edit',
44072             text: 'Switch to source editing mode.',
44073             cls: 'x-html-editor-tip'
44074         }
44075     },
44076     // private
44077     onDestroy : function(){
44078         if(this.rendered){
44079             
44080             this.tb.items.each(function(item){
44081                 if(item.menu){
44082                     item.menu.removeAll();
44083                     if(item.menu.el){
44084                         item.menu.el.destroy();
44085                     }
44086                 }
44087                 item.destroy();
44088             });
44089              
44090         }
44091     },
44092     onFirstFocus: function() {
44093         this.tb.items.each(function(item){
44094            item.enable();
44095         });
44096     }
44097 });
44098
44099
44100
44101
44102 // <script type="text/javascript">
44103 /*
44104  * Based on
44105  * Ext JS Library 1.1.1
44106  * Copyright(c) 2006-2007, Ext JS, LLC.
44107  *  
44108  
44109  */
44110
44111  
44112 /**
44113  * @class Roo.form.HtmlEditor.ToolbarContext
44114  * Context Toolbar
44115  * 
44116  * Usage:
44117  *
44118  new Roo.form.HtmlEditor({
44119     ....
44120     toolbars : [
44121         { xtype: 'ToolbarStandard', styles : {} }
44122         { xtype: 'ToolbarContext', disable : {} }
44123     ]
44124 })
44125
44126      
44127  * 
44128  * @config : {Object} disable List of elements to disable.. (not done yet.)
44129  * @config : {Object} styles  Map of styles available.
44130  * 
44131  */
44132
44133 Roo.form.HtmlEditor.ToolbarContext = function(config)
44134 {
44135     
44136     Roo.apply(this, config);
44137     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44138     // dont call parent... till later.
44139     this.styles = this.styles || {};
44140 }
44141
44142  
44143
44144 Roo.form.HtmlEditor.ToolbarContext.types = {
44145     'IMG' : {
44146         width : {
44147             title: "Width",
44148             width: 40
44149         },
44150         height:  {
44151             title: "Height",
44152             width: 40
44153         },
44154         align: {
44155             title: "Align",
44156             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44157             width : 80
44158             
44159         },
44160         border: {
44161             title: "Border",
44162             width: 40
44163         },
44164         alt: {
44165             title: "Alt",
44166             width: 120
44167         },
44168         src : {
44169             title: "Src",
44170             width: 220
44171         }
44172         
44173     },
44174     'A' : {
44175         name : {
44176             title: "Name",
44177             width: 50
44178         },
44179         target:  {
44180             title: "Target",
44181             width: 120
44182         },
44183         href:  {
44184             title: "Href",
44185             width: 220
44186         } // border?
44187         
44188     },
44189     'TABLE' : {
44190         rows : {
44191             title: "Rows",
44192             width: 20
44193         },
44194         cols : {
44195             title: "Cols",
44196             width: 20
44197         },
44198         width : {
44199             title: "Width",
44200             width: 40
44201         },
44202         height : {
44203             title: "Height",
44204             width: 40
44205         },
44206         border : {
44207             title: "Border",
44208             width: 20
44209         }
44210     },
44211     'TD' : {
44212         width : {
44213             title: "Width",
44214             width: 40
44215         },
44216         height : {
44217             title: "Height",
44218             width: 40
44219         },   
44220         align: {
44221             title: "Align",
44222             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44223             width: 80
44224         },
44225         valign: {
44226             title: "Valign",
44227             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44228             width: 80
44229         },
44230         colspan: {
44231             title: "Colspan",
44232             width: 20
44233             
44234         },
44235          'font-family'  : {
44236             title : "Font",
44237             style : 'fontFamily',
44238             displayField: 'display',
44239             optname : 'font-family',
44240             width: 140
44241         }
44242     },
44243     'INPUT' : {
44244         name : {
44245             title: "name",
44246             width: 120
44247         },
44248         value : {
44249             title: "Value",
44250             width: 120
44251         },
44252         width : {
44253             title: "Width",
44254             width: 40
44255         }
44256     },
44257     'LABEL' : {
44258         'for' : {
44259             title: "For",
44260             width: 120
44261         }
44262     },
44263     'TEXTAREA' : {
44264           name : {
44265             title: "name",
44266             width: 120
44267         },
44268         rows : {
44269             title: "Rows",
44270             width: 20
44271         },
44272         cols : {
44273             title: "Cols",
44274             width: 20
44275         }
44276     },
44277     'SELECT' : {
44278         name : {
44279             title: "name",
44280             width: 120
44281         },
44282         selectoptions : {
44283             title: "Options",
44284             width: 200
44285         }
44286     },
44287     
44288     // should we really allow this??
44289     // should this just be 
44290     'BODY' : {
44291         title : {
44292             title: "Title",
44293             width: 200,
44294             disabled : true
44295         }
44296     },
44297     'SPAN' : {
44298         'font-family'  : {
44299             title : "Font",
44300             style : 'fontFamily',
44301             displayField: 'display',
44302             optname : 'font-family',
44303             width: 140
44304         }
44305     },
44306     'DIV' : {
44307         'font-family'  : {
44308             title : "Font",
44309             style : 'fontFamily',
44310             displayField: 'display',
44311             optname : 'font-family',
44312             width: 140
44313         }
44314     },
44315      'P' : {
44316         'font-family'  : {
44317             title : "Font",
44318             style : 'fontFamily',
44319             displayField: 'display',
44320             optname : 'font-family',
44321             width: 140
44322         }
44323     },
44324     
44325     '*' : {
44326         // empty..
44327     }
44328
44329 };
44330
44331 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44332 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44333
44334 Roo.form.HtmlEditor.ToolbarContext.options = {
44335         'font-family'  : [ 
44336                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44337                 [ 'Courier New', 'Courier New'],
44338                 [ 'Tahoma', 'Tahoma'],
44339                 [ 'Times New Roman,serif', 'Times'],
44340                 [ 'Verdana','Verdana' ]
44341         ]
44342 };
44343
44344 // fixme - these need to be configurable..
44345  
44346
44347 Roo.form.HtmlEditor.ToolbarContext.types
44348
44349
44350 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44351     
44352     tb: false,
44353     
44354     rendered: false,
44355     
44356     editor : false,
44357     editorcore : false,
44358     /**
44359      * @cfg {Object} disable  List of toolbar elements to disable
44360          
44361      */
44362     disable : false,
44363     /**
44364      * @cfg {Object} styles List of styles 
44365      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44366      *
44367      * These must be defined in the page, so they get rendered correctly..
44368      * .headline { }
44369      * TD.underline { }
44370      * 
44371      */
44372     styles : false,
44373     
44374     options: false,
44375     
44376     toolbars : false,
44377     
44378     init : function(editor)
44379     {
44380         this.editor = editor;
44381         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44382         var editorcore = this.editorcore;
44383         
44384         var fid = editorcore.frameId;
44385         var etb = this;
44386         function btn(id, toggle, handler){
44387             var xid = fid + '-'+ id ;
44388             return {
44389                 id : xid,
44390                 cmd : id,
44391                 cls : 'x-btn-icon x-edit-'+id,
44392                 enableToggle:toggle !== false,
44393                 scope: editorcore, // was editor...
44394                 handler:handler||editorcore.relayBtnCmd,
44395                 clickEvent:'mousedown',
44396                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44397                 tabIndex:-1
44398             };
44399         }
44400         // create a new element.
44401         var wdiv = editor.wrap.createChild({
44402                 tag: 'div'
44403             }, editor.wrap.dom.firstChild.nextSibling, true);
44404         
44405         // can we do this more than once??
44406         
44407          // stop form submits
44408       
44409  
44410         // disable everything...
44411         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44412         this.toolbars = {};
44413            
44414         for (var i in  ty) {
44415           
44416             this.toolbars[i] = this.buildToolbar(ty[i],i);
44417         }
44418         this.tb = this.toolbars.BODY;
44419         this.tb.el.show();
44420         this.buildFooter();
44421         this.footer.show();
44422         editor.on('hide', function( ) { this.footer.hide() }, this);
44423         editor.on('show', function( ) { this.footer.show() }, this);
44424         
44425          
44426         this.rendered = true;
44427         
44428         // the all the btns;
44429         editor.on('editorevent', this.updateToolbar, this);
44430         // other toolbars need to implement this..
44431         //editor.on('editmodechange', this.updateToolbar, this);
44432     },
44433     
44434     
44435     
44436     /**
44437      * Protected method that will not generally be called directly. It triggers
44438      * a toolbar update by reading the markup state of the current selection in the editor.
44439      */
44440     updateToolbar: function(editor,ev,sel){
44441
44442         //Roo.log(ev);
44443         // capture mouse up - this is handy for selecting images..
44444         // perhaps should go somewhere else...
44445         if(!this.editorcore.activated){
44446              this.editor.onFirstFocus();
44447             return;
44448         }
44449         
44450         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44451         // selectNode - might want to handle IE?
44452         if (ev &&
44453             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44454             ev.target && ev.target.tagName == 'IMG') {
44455             // they have click on an image...
44456             // let's see if we can change the selection...
44457             sel = ev.target;
44458          
44459               var nodeRange = sel.ownerDocument.createRange();
44460             try {
44461                 nodeRange.selectNode(sel);
44462             } catch (e) {
44463                 nodeRange.selectNodeContents(sel);
44464             }
44465             //nodeRange.collapse(true);
44466             var s = this.editorcore.win.getSelection();
44467             s.removeAllRanges();
44468             s.addRange(nodeRange);
44469         }  
44470         
44471       
44472         var updateFooter = sel ? false : true;
44473         
44474         
44475         var ans = this.editorcore.getAllAncestors();
44476         
44477         // pick
44478         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44479         
44480         if (!sel) { 
44481             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44482             sel = sel ? sel : this.editorcore.doc.body;
44483             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44484             
44485         }
44486         // pick a menu that exists..
44487         var tn = sel.tagName.toUpperCase();
44488         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44489         
44490         tn = sel.tagName.toUpperCase();
44491         
44492         var lastSel = this.tb.selectedNode
44493         
44494         this.tb.selectedNode = sel;
44495         
44496         // if current menu does not match..
44497         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44498                 
44499             this.tb.el.hide();
44500             ///console.log("show: " + tn);
44501             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44502             this.tb.el.show();
44503             // update name
44504             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44505             
44506             
44507             // update attributes
44508             if (this.tb.fields) {
44509                 this.tb.fields.each(function(e) {
44510                     if (e.stylename) {
44511                         e.setValue(sel.style[e.stylename]);
44512                         return;
44513                     } 
44514                    e.setValue(sel.getAttribute(e.attrname));
44515                 });
44516             }
44517             
44518             var hasStyles = false;
44519             for(var i in this.styles) {
44520                 hasStyles = true;
44521                 break;
44522             }
44523             
44524             // update styles
44525             if (hasStyles) { 
44526                 var st = this.tb.fields.item(0);
44527                 
44528                 st.store.removeAll();
44529                
44530                 
44531                 var cn = sel.className.split(/\s+/);
44532                 
44533                 var avs = [];
44534                 if (this.styles['*']) {
44535                     
44536                     Roo.each(this.styles['*'], function(v) {
44537                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44538                     });
44539                 }
44540                 if (this.styles[tn]) { 
44541                     Roo.each(this.styles[tn], function(v) {
44542                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44543                     });
44544                 }
44545                 
44546                 st.store.loadData(avs);
44547                 st.collapse();
44548                 st.setValue(cn);
44549             }
44550             // flag our selected Node.
44551             this.tb.selectedNode = sel;
44552            
44553            
44554             Roo.menu.MenuMgr.hideAll();
44555
44556         }
44557         
44558         if (!updateFooter) {
44559             //this.footDisp.dom.innerHTML = ''; 
44560             return;
44561         }
44562         // update the footer
44563         //
44564         var html = '';
44565         
44566         this.footerEls = ans.reverse();
44567         Roo.each(this.footerEls, function(a,i) {
44568             if (!a) { return; }
44569             html += html.length ? ' &gt; '  :  '';
44570             
44571             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44572             
44573         });
44574        
44575         // 
44576         var sz = this.footDisp.up('td').getSize();
44577         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44578         this.footDisp.dom.style.marginLeft = '5px';
44579         
44580         this.footDisp.dom.style.overflow = 'hidden';
44581         
44582         this.footDisp.dom.innerHTML = html;
44583             
44584         //this.editorsyncValue();
44585     },
44586      
44587     
44588    
44589        
44590     // private
44591     onDestroy : function(){
44592         if(this.rendered){
44593             
44594             this.tb.items.each(function(item){
44595                 if(item.menu){
44596                     item.menu.removeAll();
44597                     if(item.menu.el){
44598                         item.menu.el.destroy();
44599                     }
44600                 }
44601                 item.destroy();
44602             });
44603              
44604         }
44605     },
44606     onFirstFocus: function() {
44607         // need to do this for all the toolbars..
44608         this.tb.items.each(function(item){
44609            item.enable();
44610         });
44611     },
44612     buildToolbar: function(tlist, nm)
44613     {
44614         var editor = this.editor;
44615         var editorcore = this.editorcore;
44616          // create a new element.
44617         var wdiv = editor.wrap.createChild({
44618                 tag: 'div'
44619             }, editor.wrap.dom.firstChild.nextSibling, true);
44620         
44621        
44622         var tb = new Roo.Toolbar(wdiv);
44623         // add the name..
44624         
44625         tb.add(nm+ ":&nbsp;");
44626         
44627         var styles = [];
44628         for(var i in this.styles) {
44629             styles.push(i);
44630         }
44631         
44632         // styles...
44633         if (styles && styles.length) {
44634             
44635             // this needs a multi-select checkbox...
44636             tb.addField( new Roo.form.ComboBox({
44637                 store: new Roo.data.SimpleStore({
44638                     id : 'val',
44639                     fields: ['val', 'selected'],
44640                     data : [] 
44641                 }),
44642                 name : '-roo-edit-className',
44643                 attrname : 'className',
44644                 displayField: 'val',
44645                 typeAhead: false,
44646                 mode: 'local',
44647                 editable : false,
44648                 triggerAction: 'all',
44649                 emptyText:'Select Style',
44650                 selectOnFocus:true,
44651                 width: 130,
44652                 listeners : {
44653                     'select': function(c, r, i) {
44654                         // initial support only for on class per el..
44655                         tb.selectedNode.className =  r ? r.get('val') : '';
44656                         editorcore.syncValue();
44657                     }
44658                 }
44659     
44660             }));
44661         }
44662         
44663         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44664         var tbops = tbc.options;
44665         
44666         for (var i in tlist) {
44667             
44668             var item = tlist[i];
44669             tb.add(item.title + ":&nbsp;");
44670             
44671             
44672             //optname == used so you can configure the options available..
44673             var opts = item.opts ? item.opts : false;
44674             if (item.optname) {
44675                 opts = tbops[item.optname];
44676            
44677             }
44678             
44679             if (opts) {
44680                 // opts == pulldown..
44681                 tb.addField( new Roo.form.ComboBox({
44682                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44683                         id : 'val',
44684                         fields: ['val', 'display'],
44685                         data : opts  
44686                     }),
44687                     name : '-roo-edit-' + i,
44688                     attrname : i,
44689                     stylename : item.style ? item.style : false,
44690                     displayField: item.displayField ? item.displayField : 'val',
44691                     valueField :  'val',
44692                     typeAhead: false,
44693                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44694                     editable : false,
44695                     triggerAction: 'all',
44696                     emptyText:'Select',
44697                     selectOnFocus:true,
44698                     width: item.width ? item.width  : 130,
44699                     listeners : {
44700                         'select': function(c, r, i) {
44701                             if (c.stylename) {
44702                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44703                                 return;
44704                             }
44705                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44706                         }
44707                     }
44708
44709                 }));
44710                 continue;
44711                     
44712                  
44713                 
44714                 tb.addField( new Roo.form.TextField({
44715                     name: i,
44716                     width: 100,
44717                     //allowBlank:false,
44718                     value: ''
44719                 }));
44720                 continue;
44721             }
44722             tb.addField( new Roo.form.TextField({
44723                 name: '-roo-edit-' + i,
44724                 attrname : i,
44725                 
44726                 width: item.width,
44727                 //allowBlank:true,
44728                 value: '',
44729                 listeners: {
44730                     'change' : function(f, nv, ov) {
44731                         tb.selectedNode.setAttribute(f.attrname, nv);
44732                     }
44733                 }
44734             }));
44735              
44736         }
44737         
44738         var _this = this;
44739         
44740         if(nm == 'BODY'){
44741             tb.addSeparator();
44742         
44743             tb.addButton( {
44744                 text: 'Stylesheets',
44745
44746                 listeners : {
44747                     click : function ()
44748                     {
44749                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44750                     }
44751                 }
44752             });
44753         }
44754         
44755         tb.addFill();
44756         tb.addButton( {
44757             text: 'Remove Tag',
44758     
44759             listeners : {
44760                 click : function ()
44761                 {
44762                     // remove
44763                     // undo does not work.
44764                      
44765                     var sn = tb.selectedNode;
44766                     
44767                     var pn = sn.parentNode;
44768                     
44769                     var stn =  sn.childNodes[0];
44770                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44771                     while (sn.childNodes.length) {
44772                         var node = sn.childNodes[0];
44773                         sn.removeChild(node);
44774                         //Roo.log(node);
44775                         pn.insertBefore(node, sn);
44776                         
44777                     }
44778                     pn.removeChild(sn);
44779                     var range = editorcore.createRange();
44780         
44781                     range.setStart(stn,0);
44782                     range.setEnd(en,0); //????
44783                     //range.selectNode(sel);
44784                     
44785                     
44786                     var selection = editorcore.getSelection();
44787                     selection.removeAllRanges();
44788                     selection.addRange(range);
44789                     
44790                     
44791                     
44792                     //_this.updateToolbar(null, null, pn);
44793                     _this.updateToolbar(null, null, null);
44794                     _this.footDisp.dom.innerHTML = ''; 
44795                 }
44796             }
44797             
44798                     
44799                 
44800             
44801         });
44802         
44803         
44804         tb.el.on('click', function(e){
44805             e.preventDefault(); // what does this do?
44806         });
44807         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44808         tb.el.hide();
44809         tb.name = nm;
44810         // dont need to disable them... as they will get hidden
44811         return tb;
44812          
44813         
44814     },
44815     buildFooter : function()
44816     {
44817         
44818         var fel = this.editor.wrap.createChild();
44819         this.footer = new Roo.Toolbar(fel);
44820         // toolbar has scrolly on left / right?
44821         var footDisp= new Roo.Toolbar.Fill();
44822         var _t = this;
44823         this.footer.add(
44824             {
44825                 text : '&lt;',
44826                 xtype: 'Button',
44827                 handler : function() {
44828                     _t.footDisp.scrollTo('left',0,true)
44829                 }
44830             }
44831         );
44832         this.footer.add( footDisp );
44833         this.footer.add( 
44834             {
44835                 text : '&gt;',
44836                 xtype: 'Button',
44837                 handler : function() {
44838                     // no animation..
44839                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44840                 }
44841             }
44842         );
44843         var fel = Roo.get(footDisp.el);
44844         fel.addClass('x-editor-context');
44845         this.footDispWrap = fel; 
44846         this.footDispWrap.overflow  = 'hidden';
44847         
44848         this.footDisp = fel.createChild();
44849         this.footDispWrap.on('click', this.onContextClick, this)
44850         
44851         
44852     },
44853     onContextClick : function (ev,dom)
44854     {
44855         ev.preventDefault();
44856         var  cn = dom.className;
44857         //Roo.log(cn);
44858         if (!cn.match(/x-ed-loc-/)) {
44859             return;
44860         }
44861         var n = cn.split('-').pop();
44862         var ans = this.footerEls;
44863         var sel = ans[n];
44864         
44865          // pick
44866         var range = this.editorcore.createRange();
44867         
44868         range.selectNodeContents(sel);
44869         //range.selectNode(sel);
44870         
44871         
44872         var selection = this.editorcore.getSelection();
44873         selection.removeAllRanges();
44874         selection.addRange(range);
44875         
44876         
44877         
44878         this.updateToolbar(null, null, sel);
44879         
44880         
44881     }
44882     
44883     
44884     
44885     
44886     
44887 });
44888
44889
44890
44891
44892
44893 /*
44894  * Based on:
44895  * Ext JS Library 1.1.1
44896  * Copyright(c) 2006-2007, Ext JS, LLC.
44897  *
44898  * Originally Released Under LGPL - original licence link has changed is not relivant.
44899  *
44900  * Fork - LGPL
44901  * <script type="text/javascript">
44902  */
44903  
44904 /**
44905  * @class Roo.form.BasicForm
44906  * @extends Roo.util.Observable
44907  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44908  * @constructor
44909  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44910  * @param {Object} config Configuration options
44911  */
44912 Roo.form.BasicForm = function(el, config){
44913     this.allItems = [];
44914     this.childForms = [];
44915     Roo.apply(this, config);
44916     /*
44917      * The Roo.form.Field items in this form.
44918      * @type MixedCollection
44919      */
44920      
44921      
44922     this.items = new Roo.util.MixedCollection(false, function(o){
44923         return o.id || (o.id = Roo.id());
44924     });
44925     this.addEvents({
44926         /**
44927          * @event beforeaction
44928          * Fires before any action is performed. Return false to cancel the action.
44929          * @param {Form} this
44930          * @param {Action} action The action to be performed
44931          */
44932         beforeaction: true,
44933         /**
44934          * @event actionfailed
44935          * Fires when an action fails.
44936          * @param {Form} this
44937          * @param {Action} action The action that failed
44938          */
44939         actionfailed : true,
44940         /**
44941          * @event actioncomplete
44942          * Fires when an action is completed.
44943          * @param {Form} this
44944          * @param {Action} action The action that completed
44945          */
44946         actioncomplete : true
44947     });
44948     if(el){
44949         this.initEl(el);
44950     }
44951     Roo.form.BasicForm.superclass.constructor.call(this);
44952 };
44953
44954 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44955     /**
44956      * @cfg {String} method
44957      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44958      */
44959     /**
44960      * @cfg {DataReader} reader
44961      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44962      * This is optional as there is built-in support for processing JSON.
44963      */
44964     /**
44965      * @cfg {DataReader} errorReader
44966      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44967      * This is completely optional as there is built-in support for processing JSON.
44968      */
44969     /**
44970      * @cfg {String} url
44971      * The URL to use for form actions if one isn't supplied in the action options.
44972      */
44973     /**
44974      * @cfg {Boolean} fileUpload
44975      * Set to true if this form is a file upload.
44976      */
44977      
44978     /**
44979      * @cfg {Object} baseParams
44980      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44981      */
44982      /**
44983      
44984     /**
44985      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44986      */
44987     timeout: 30,
44988
44989     // private
44990     activeAction : null,
44991
44992     /**
44993      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44994      * or setValues() data instead of when the form was first created.
44995      */
44996     trackResetOnLoad : false,
44997     
44998     
44999     /**
45000      * childForms - used for multi-tab forms
45001      * @type {Array}
45002      */
45003     childForms : false,
45004     
45005     /**
45006      * allItems - full list of fields.
45007      * @type {Array}
45008      */
45009     allItems : false,
45010     
45011     /**
45012      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45013      * element by passing it or its id or mask the form itself by passing in true.
45014      * @type Mixed
45015      */
45016     waitMsgTarget : false,
45017
45018     // private
45019     initEl : function(el){
45020         this.el = Roo.get(el);
45021         this.id = this.el.id || Roo.id();
45022         this.el.on('submit', this.onSubmit, this);
45023         this.el.addClass('x-form');
45024     },
45025
45026     // private
45027     onSubmit : function(e){
45028         e.stopEvent();
45029     },
45030
45031     /**
45032      * Returns true if client-side validation on the form is successful.
45033      * @return Boolean
45034      */
45035     isValid : function(){
45036         var valid = true;
45037         this.items.each(function(f){
45038            if(!f.validate()){
45039                valid = false;
45040            }
45041         });
45042         return valid;
45043     },
45044
45045     /**
45046      * Returns true if any fields in this form have changed since their original load.
45047      * @return Boolean
45048      */
45049     isDirty : function(){
45050         var dirty = false;
45051         this.items.each(function(f){
45052            if(f.isDirty()){
45053                dirty = true;
45054                return false;
45055            }
45056         });
45057         return dirty;
45058     },
45059
45060     /**
45061      * Performs a predefined action (submit or load) or custom actions you define on this form.
45062      * @param {String} actionName The name of the action type
45063      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45064      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45065      * accept other config options):
45066      * <pre>
45067 Property          Type             Description
45068 ----------------  ---------------  ----------------------------------------------------------------------------------
45069 url               String           The url for the action (defaults to the form's url)
45070 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45071 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45072 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45073                                    validate the form on the client (defaults to false)
45074      * </pre>
45075      * @return {BasicForm} this
45076      */
45077     doAction : function(action, options){
45078         if(typeof action == 'string'){
45079             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45080         }
45081         if(this.fireEvent('beforeaction', this, action) !== false){
45082             this.beforeAction(action);
45083             action.run.defer(100, action);
45084         }
45085         return this;
45086     },
45087
45088     /**
45089      * Shortcut to do a submit action.
45090      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45091      * @return {BasicForm} this
45092      */
45093     submit : function(options){
45094         this.doAction('submit', options);
45095         return this;
45096     },
45097
45098     /**
45099      * Shortcut to do a load action.
45100      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45101      * @return {BasicForm} this
45102      */
45103     load : function(options){
45104         this.doAction('load', options);
45105         return this;
45106     },
45107
45108     /**
45109      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45110      * @param {Record} record The record to edit
45111      * @return {BasicForm} this
45112      */
45113     updateRecord : function(record){
45114         record.beginEdit();
45115         var fs = record.fields;
45116         fs.each(function(f){
45117             var field = this.findField(f.name);
45118             if(field){
45119                 record.set(f.name, field.getValue());
45120             }
45121         }, this);
45122         record.endEdit();
45123         return this;
45124     },
45125
45126     /**
45127      * Loads an Roo.data.Record into this form.
45128      * @param {Record} record The record to load
45129      * @return {BasicForm} this
45130      */
45131     loadRecord : function(record){
45132         this.setValues(record.data);
45133         return this;
45134     },
45135
45136     // private
45137     beforeAction : function(action){
45138         var o = action.options;
45139         
45140        
45141         if(this.waitMsgTarget === true){
45142             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45143         }else if(this.waitMsgTarget){
45144             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45145             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45146         }else {
45147             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45148         }
45149          
45150     },
45151
45152     // private
45153     afterAction : function(action, success){
45154         this.activeAction = null;
45155         var o = action.options;
45156         
45157         if(this.waitMsgTarget === true){
45158             this.el.unmask();
45159         }else if(this.waitMsgTarget){
45160             this.waitMsgTarget.unmask();
45161         }else{
45162             Roo.MessageBox.updateProgress(1);
45163             Roo.MessageBox.hide();
45164         }
45165          
45166         if(success){
45167             if(o.reset){
45168                 this.reset();
45169             }
45170             Roo.callback(o.success, o.scope, [this, action]);
45171             this.fireEvent('actioncomplete', this, action);
45172             
45173         }else{
45174             
45175             // failure condition..
45176             // we have a scenario where updates need confirming.
45177             // eg. if a locking scenario exists..
45178             // we look for { errors : { needs_confirm : true }} in the response.
45179             if (
45180                 (typeof(action.result) != 'undefined')  &&
45181                 (typeof(action.result.errors) != 'undefined')  &&
45182                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45183            ){
45184                 var _t = this;
45185                 Roo.MessageBox.confirm(
45186                     "Change requires confirmation",
45187                     action.result.errorMsg,
45188                     function(r) {
45189                         if (r != 'yes') {
45190                             return;
45191                         }
45192                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45193                     }
45194                     
45195                 );
45196                 
45197                 
45198                 
45199                 return;
45200             }
45201             
45202             Roo.callback(o.failure, o.scope, [this, action]);
45203             // show an error message if no failed handler is set..
45204             if (!this.hasListener('actionfailed')) {
45205                 Roo.MessageBox.alert("Error",
45206                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45207                         action.result.errorMsg :
45208                         "Saving Failed, please check your entries or try again"
45209                 );
45210             }
45211             
45212             this.fireEvent('actionfailed', this, action);
45213         }
45214         
45215     },
45216
45217     /**
45218      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45219      * @param {String} id The value to search for
45220      * @return Field
45221      */
45222     findField : function(id){
45223         var field = this.items.get(id);
45224         if(!field){
45225             this.items.each(function(f){
45226                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45227                     field = f;
45228                     return false;
45229                 }
45230             });
45231         }
45232         return field || null;
45233     },
45234
45235     /**
45236      * Add a secondary form to this one, 
45237      * Used to provide tabbed forms. One form is primary, with hidden values 
45238      * which mirror the elements from the other forms.
45239      * 
45240      * @param {Roo.form.Form} form to add.
45241      * 
45242      */
45243     addForm : function(form)
45244     {
45245        
45246         if (this.childForms.indexOf(form) > -1) {
45247             // already added..
45248             return;
45249         }
45250         this.childForms.push(form);
45251         var n = '';
45252         Roo.each(form.allItems, function (fe) {
45253             
45254             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45255             if (this.findField(n)) { // already added..
45256                 return;
45257             }
45258             var add = new Roo.form.Hidden({
45259                 name : n
45260             });
45261             add.render(this.el);
45262             
45263             this.add( add );
45264         }, this);
45265         
45266     },
45267     /**
45268      * Mark fields in this form invalid in bulk.
45269      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45270      * @return {BasicForm} this
45271      */
45272     markInvalid : function(errors){
45273         if(errors instanceof Array){
45274             for(var i = 0, len = errors.length; i < len; i++){
45275                 var fieldError = errors[i];
45276                 var f = this.findField(fieldError.id);
45277                 if(f){
45278                     f.markInvalid(fieldError.msg);
45279                 }
45280             }
45281         }else{
45282             var field, id;
45283             for(id in errors){
45284                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45285                     field.markInvalid(errors[id]);
45286                 }
45287             }
45288         }
45289         Roo.each(this.childForms || [], function (f) {
45290             f.markInvalid(errors);
45291         });
45292         
45293         return this;
45294     },
45295
45296     /**
45297      * Set values for fields in this form in bulk.
45298      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45299      * @return {BasicForm} this
45300      */
45301     setValues : function(values){
45302         if(values instanceof Array){ // array of objects
45303             for(var i = 0, len = values.length; i < len; i++){
45304                 var v = values[i];
45305                 var f = this.findField(v.id);
45306                 if(f){
45307                     f.setValue(v.value);
45308                     if(this.trackResetOnLoad){
45309                         f.originalValue = f.getValue();
45310                     }
45311                 }
45312             }
45313         }else{ // object hash
45314             var field, id;
45315             for(id in values){
45316                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45317                     
45318                     if (field.setFromData && 
45319                         field.valueField && 
45320                         field.displayField &&
45321                         // combos' with local stores can 
45322                         // be queried via setValue()
45323                         // to set their value..
45324                         (field.store && !field.store.isLocal)
45325                         ) {
45326                         // it's a combo
45327                         var sd = { };
45328                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45329                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45330                         field.setFromData(sd);
45331                         
45332                     } else {
45333                         field.setValue(values[id]);
45334                     }
45335                     
45336                     
45337                     if(this.trackResetOnLoad){
45338                         field.originalValue = field.getValue();
45339                     }
45340                 }
45341             }
45342         }
45343          
45344         Roo.each(this.childForms || [], function (f) {
45345             f.setValues(values);
45346         });
45347                 
45348         return this;
45349     },
45350
45351     /**
45352      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45353      * they are returned as an array.
45354      * @param {Boolean} asString
45355      * @return {Object}
45356      */
45357     getValues : function(asString){
45358         if (this.childForms) {
45359             // copy values from the child forms
45360             Roo.each(this.childForms, function (f) {
45361                 this.setValues(f.getValues());
45362             }, this);
45363         }
45364         
45365         
45366         
45367         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45368         if(asString === true){
45369             return fs;
45370         }
45371         return Roo.urlDecode(fs);
45372     },
45373     
45374     /**
45375      * Returns the fields in this form as an object with key/value pairs. 
45376      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45377      * @return {Object}
45378      */
45379     getFieldValues : function(with_hidden)
45380     {
45381         if (this.childForms) {
45382             // copy values from the child forms
45383             // should this call getFieldValues - probably not as we do not currently copy
45384             // hidden fields when we generate..
45385             Roo.each(this.childForms, function (f) {
45386                 this.setValues(f.getValues());
45387             }, this);
45388         }
45389         
45390         var ret = {};
45391         this.items.each(function(f){
45392             if (!f.getName()) {
45393                 return;
45394             }
45395             var v = f.getValue();
45396             if (f.inputType =='radio') {
45397                 if (typeof(ret[f.getName()]) == 'undefined') {
45398                     ret[f.getName()] = ''; // empty..
45399                 }
45400                 
45401                 if (!f.el.dom.checked) {
45402                     return;
45403                     
45404                 }
45405                 v = f.el.dom.value;
45406                 
45407             }
45408             
45409             // not sure if this supported any more..
45410             if ((typeof(v) == 'object') && f.getRawValue) {
45411                 v = f.getRawValue() ; // dates..
45412             }
45413             // combo boxes where name != hiddenName...
45414             if (f.name != f.getName()) {
45415                 ret[f.name] = f.getRawValue();
45416             }
45417             ret[f.getName()] = v;
45418         });
45419         
45420         return ret;
45421     },
45422
45423     /**
45424      * Clears all invalid messages in this form.
45425      * @return {BasicForm} this
45426      */
45427     clearInvalid : function(){
45428         this.items.each(function(f){
45429            f.clearInvalid();
45430         });
45431         
45432         Roo.each(this.childForms || [], function (f) {
45433             f.clearInvalid();
45434         });
45435         
45436         
45437         return this;
45438     },
45439
45440     /**
45441      * Resets this form.
45442      * @return {BasicForm} this
45443      */
45444     reset : function(){
45445         this.items.each(function(f){
45446             f.reset();
45447         });
45448         
45449         Roo.each(this.childForms || [], function (f) {
45450             f.reset();
45451         });
45452        
45453         
45454         return this;
45455     },
45456
45457     /**
45458      * Add Roo.form components to this form.
45459      * @param {Field} field1
45460      * @param {Field} field2 (optional)
45461      * @param {Field} etc (optional)
45462      * @return {BasicForm} this
45463      */
45464     add : function(){
45465         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45466         return this;
45467     },
45468
45469
45470     /**
45471      * Removes a field from the items collection (does NOT remove its markup).
45472      * @param {Field} field
45473      * @return {BasicForm} this
45474      */
45475     remove : function(field){
45476         this.items.remove(field);
45477         return this;
45478     },
45479
45480     /**
45481      * Looks at the fields in this form, checks them for an id attribute,
45482      * and calls applyTo on the existing dom element with that id.
45483      * @return {BasicForm} this
45484      */
45485     render : function(){
45486         this.items.each(function(f){
45487             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45488                 f.applyTo(f.id);
45489             }
45490         });
45491         return this;
45492     },
45493
45494     /**
45495      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45496      * @param {Object} values
45497      * @return {BasicForm} this
45498      */
45499     applyToFields : function(o){
45500         this.items.each(function(f){
45501            Roo.apply(f, o);
45502         });
45503         return this;
45504     },
45505
45506     /**
45507      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45508      * @param {Object} values
45509      * @return {BasicForm} this
45510      */
45511     applyIfToFields : function(o){
45512         this.items.each(function(f){
45513            Roo.applyIf(f, o);
45514         });
45515         return this;
45516     }
45517 });
45518
45519 // back compat
45520 Roo.BasicForm = Roo.form.BasicForm;/*
45521  * Based on:
45522  * Ext JS Library 1.1.1
45523  * Copyright(c) 2006-2007, Ext JS, LLC.
45524  *
45525  * Originally Released Under LGPL - original licence link has changed is not relivant.
45526  *
45527  * Fork - LGPL
45528  * <script type="text/javascript">
45529  */
45530
45531 /**
45532  * @class Roo.form.Form
45533  * @extends Roo.form.BasicForm
45534  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45535  * @constructor
45536  * @param {Object} config Configuration options
45537  */
45538 Roo.form.Form = function(config){
45539     var xitems =  [];
45540     if (config.items) {
45541         xitems = config.items;
45542         delete config.items;
45543     }
45544    
45545     
45546     Roo.form.Form.superclass.constructor.call(this, null, config);
45547     this.url = this.url || this.action;
45548     if(!this.root){
45549         this.root = new Roo.form.Layout(Roo.applyIf({
45550             id: Roo.id()
45551         }, config));
45552     }
45553     this.active = this.root;
45554     /**
45555      * Array of all the buttons that have been added to this form via {@link addButton}
45556      * @type Array
45557      */
45558     this.buttons = [];
45559     this.allItems = [];
45560     this.addEvents({
45561         /**
45562          * @event clientvalidation
45563          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45564          * @param {Form} this
45565          * @param {Boolean} valid true if the form has passed client-side validation
45566          */
45567         clientvalidation: true,
45568         /**
45569          * @event rendered
45570          * Fires when the form is rendered
45571          * @param {Roo.form.Form} form
45572          */
45573         rendered : true
45574     });
45575     
45576     if (this.progressUrl) {
45577             // push a hidden field onto the list of fields..
45578             this.addxtype( {
45579                     xns: Roo.form, 
45580                     xtype : 'Hidden', 
45581                     name : 'UPLOAD_IDENTIFIER' 
45582             });
45583         }
45584         
45585     
45586     Roo.each(xitems, this.addxtype, this);
45587     
45588     
45589     
45590 };
45591
45592 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45593     /**
45594      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45595      */
45596     /**
45597      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45598      */
45599     /**
45600      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45601      */
45602     buttonAlign:'center',
45603
45604     /**
45605      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45606      */
45607     minButtonWidth:75,
45608
45609     /**
45610      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45611      * This property cascades to child containers if not set.
45612      */
45613     labelAlign:'left',
45614
45615     /**
45616      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45617      * fires a looping event with that state. This is required to bind buttons to the valid
45618      * state using the config value formBind:true on the button.
45619      */
45620     monitorValid : false,
45621
45622     /**
45623      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45624      */
45625     monitorPoll : 200,
45626     
45627     /**
45628      * @cfg {String} progressUrl - Url to return progress data 
45629      */
45630     
45631     progressUrl : false,
45632   
45633     /**
45634      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45635      * fields are added and the column is closed. If no fields are passed the column remains open
45636      * until end() is called.
45637      * @param {Object} config The config to pass to the column
45638      * @param {Field} field1 (optional)
45639      * @param {Field} field2 (optional)
45640      * @param {Field} etc (optional)
45641      * @return Column The column container object
45642      */
45643     column : function(c){
45644         var col = new Roo.form.Column(c);
45645         this.start(col);
45646         if(arguments.length > 1){ // duplicate code required because of Opera
45647             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45648             this.end();
45649         }
45650         return col;
45651     },
45652
45653     /**
45654      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45655      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45656      * until end() is called.
45657      * @param {Object} config The config to pass to the fieldset
45658      * @param {Field} field1 (optional)
45659      * @param {Field} field2 (optional)
45660      * @param {Field} etc (optional)
45661      * @return FieldSet The fieldset container object
45662      */
45663     fieldset : function(c){
45664         var fs = new Roo.form.FieldSet(c);
45665         this.start(fs);
45666         if(arguments.length > 1){ // duplicate code required because of Opera
45667             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45668             this.end();
45669         }
45670         return fs;
45671     },
45672
45673     /**
45674      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45675      * fields are added and the container is closed. If no fields are passed the container remains open
45676      * until end() is called.
45677      * @param {Object} config The config to pass to the Layout
45678      * @param {Field} field1 (optional)
45679      * @param {Field} field2 (optional)
45680      * @param {Field} etc (optional)
45681      * @return Layout The container object
45682      */
45683     container : function(c){
45684         var l = new Roo.form.Layout(c);
45685         this.start(l);
45686         if(arguments.length > 1){ // duplicate code required because of Opera
45687             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45688             this.end();
45689         }
45690         return l;
45691     },
45692
45693     /**
45694      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45695      * @param {Object} container A Roo.form.Layout or subclass of Layout
45696      * @return {Form} this
45697      */
45698     start : function(c){
45699         // cascade label info
45700         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45701         this.active.stack.push(c);
45702         c.ownerCt = this.active;
45703         this.active = c;
45704         return this;
45705     },
45706
45707     /**
45708      * Closes the current open container
45709      * @return {Form} this
45710      */
45711     end : function(){
45712         if(this.active == this.root){
45713             return this;
45714         }
45715         this.active = this.active.ownerCt;
45716         return this;
45717     },
45718
45719     /**
45720      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45721      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45722      * as the label of the field.
45723      * @param {Field} field1
45724      * @param {Field} field2 (optional)
45725      * @param {Field} etc. (optional)
45726      * @return {Form} this
45727      */
45728     add : function(){
45729         this.active.stack.push.apply(this.active.stack, arguments);
45730         this.allItems.push.apply(this.allItems,arguments);
45731         var r = [];
45732         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45733             if(a[i].isFormField){
45734                 r.push(a[i]);
45735             }
45736         }
45737         if(r.length > 0){
45738             Roo.form.Form.superclass.add.apply(this, r);
45739         }
45740         return this;
45741     },
45742     
45743
45744     
45745     
45746     
45747      /**
45748      * Find any element that has been added to a form, using it's ID or name
45749      * This can include framesets, columns etc. along with regular fields..
45750      * @param {String} id - id or name to find.
45751      
45752      * @return {Element} e - or false if nothing found.
45753      */
45754     findbyId : function(id)
45755     {
45756         var ret = false;
45757         if (!id) {
45758             return ret;
45759         }
45760         Roo.each(this.allItems, function(f){
45761             if (f.id == id || f.name == id ){
45762                 ret = f;
45763                 return false;
45764             }
45765         });
45766         return ret;
45767     },
45768
45769     
45770     
45771     /**
45772      * Render this form into the passed container. This should only be called once!
45773      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45774      * @return {Form} this
45775      */
45776     render : function(ct)
45777     {
45778         
45779         
45780         
45781         ct = Roo.get(ct);
45782         var o = this.autoCreate || {
45783             tag: 'form',
45784             method : this.method || 'POST',
45785             id : this.id || Roo.id()
45786         };
45787         this.initEl(ct.createChild(o));
45788
45789         this.root.render(this.el);
45790         
45791        
45792              
45793         this.items.each(function(f){
45794             f.render('x-form-el-'+f.id);
45795         });
45796
45797         if(this.buttons.length > 0){
45798             // tables are required to maintain order and for correct IE layout
45799             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45800                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45801                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45802             }}, null, true);
45803             var tr = tb.getElementsByTagName('tr')[0];
45804             for(var i = 0, len = this.buttons.length; i < len; i++) {
45805                 var b = this.buttons[i];
45806                 var td = document.createElement('td');
45807                 td.className = 'x-form-btn-td';
45808                 b.render(tr.appendChild(td));
45809             }
45810         }
45811         if(this.monitorValid){ // initialize after render
45812             this.startMonitoring();
45813         }
45814         this.fireEvent('rendered', this);
45815         return this;
45816     },
45817
45818     /**
45819      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45820      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45821      * object or a valid Roo.DomHelper element config
45822      * @param {Function} handler The function called when the button is clicked
45823      * @param {Object} scope (optional) The scope of the handler function
45824      * @return {Roo.Button}
45825      */
45826     addButton : function(config, handler, scope){
45827         var bc = {
45828             handler: handler,
45829             scope: scope,
45830             minWidth: this.minButtonWidth,
45831             hideParent:true
45832         };
45833         if(typeof config == "string"){
45834             bc.text = config;
45835         }else{
45836             Roo.apply(bc, config);
45837         }
45838         var btn = new Roo.Button(null, bc);
45839         this.buttons.push(btn);
45840         return btn;
45841     },
45842
45843      /**
45844      * Adds a series of form elements (using the xtype property as the factory method.
45845      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45846      * @param {Object} config 
45847      */
45848     
45849     addxtype : function()
45850     {
45851         var ar = Array.prototype.slice.call(arguments, 0);
45852         var ret = false;
45853         for(var i = 0; i < ar.length; i++) {
45854             if (!ar[i]) {
45855                 continue; // skip -- if this happends something invalid got sent, we 
45856                 // should ignore it, as basically that interface element will not show up
45857                 // and that should be pretty obvious!!
45858             }
45859             
45860             if (Roo.form[ar[i].xtype]) {
45861                 ar[i].form = this;
45862                 var fe = Roo.factory(ar[i], Roo.form);
45863                 if (!ret) {
45864                     ret = fe;
45865                 }
45866                 fe.form = this;
45867                 if (fe.store) {
45868                     fe.store.form = this;
45869                 }
45870                 if (fe.isLayout) {  
45871                          
45872                     this.start(fe);
45873                     this.allItems.push(fe);
45874                     if (fe.items && fe.addxtype) {
45875                         fe.addxtype.apply(fe, fe.items);
45876                         delete fe.items;
45877                     }
45878                      this.end();
45879                     continue;
45880                 }
45881                 
45882                 
45883                  
45884                 this.add(fe);
45885               //  console.log('adding ' + ar[i].xtype);
45886             }
45887             if (ar[i].xtype == 'Button') {  
45888                 //console.log('adding button');
45889                 //console.log(ar[i]);
45890                 this.addButton(ar[i]);
45891                 this.allItems.push(fe);
45892                 continue;
45893             }
45894             
45895             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45896                 alert('end is not supported on xtype any more, use items');
45897             //    this.end();
45898             //    //console.log('adding end');
45899             }
45900             
45901         }
45902         return ret;
45903     },
45904     
45905     /**
45906      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45907      * option "monitorValid"
45908      */
45909     startMonitoring : function(){
45910         if(!this.bound){
45911             this.bound = true;
45912             Roo.TaskMgr.start({
45913                 run : this.bindHandler,
45914                 interval : this.monitorPoll || 200,
45915                 scope: this
45916             });
45917         }
45918     },
45919
45920     /**
45921      * Stops monitoring of the valid state of this form
45922      */
45923     stopMonitoring : function(){
45924         this.bound = false;
45925     },
45926
45927     // private
45928     bindHandler : function(){
45929         if(!this.bound){
45930             return false; // stops binding
45931         }
45932         var valid = true;
45933         this.items.each(function(f){
45934             if(!f.isValid(true)){
45935                 valid = false;
45936                 return false;
45937             }
45938         });
45939         for(var i = 0, len = this.buttons.length; i < len; i++){
45940             var btn = this.buttons[i];
45941             if(btn.formBind === true && btn.disabled === valid){
45942                 btn.setDisabled(!valid);
45943             }
45944         }
45945         this.fireEvent('clientvalidation', this, valid);
45946     }
45947     
45948     
45949     
45950     
45951     
45952     
45953     
45954     
45955 });
45956
45957
45958 // back compat
45959 Roo.Form = Roo.form.Form;
45960 /*
45961  * Based on:
45962  * Ext JS Library 1.1.1
45963  * Copyright(c) 2006-2007, Ext JS, LLC.
45964  *
45965  * Originally Released Under LGPL - original licence link has changed is not relivant.
45966  *
45967  * Fork - LGPL
45968  * <script type="text/javascript">
45969  */
45970
45971 // as we use this in bootstrap.
45972 Roo.namespace('Roo.form');
45973  /**
45974  * @class Roo.form.Action
45975  * Internal Class used to handle form actions
45976  * @constructor
45977  * @param {Roo.form.BasicForm} el The form element or its id
45978  * @param {Object} config Configuration options
45979  */
45980
45981  
45982  
45983 // define the action interface
45984 Roo.form.Action = function(form, options){
45985     this.form = form;
45986     this.options = options || {};
45987 };
45988 /**
45989  * Client Validation Failed
45990  * @const 
45991  */
45992 Roo.form.Action.CLIENT_INVALID = 'client';
45993 /**
45994  * Server Validation Failed
45995  * @const 
45996  */
45997 Roo.form.Action.SERVER_INVALID = 'server';
45998  /**
45999  * Connect to Server Failed
46000  * @const 
46001  */
46002 Roo.form.Action.CONNECT_FAILURE = 'connect';
46003 /**
46004  * Reading Data from Server Failed
46005  * @const 
46006  */
46007 Roo.form.Action.LOAD_FAILURE = 'load';
46008
46009 Roo.form.Action.prototype = {
46010     type : 'default',
46011     failureType : undefined,
46012     response : undefined,
46013     result : undefined,
46014
46015     // interface method
46016     run : function(options){
46017
46018     },
46019
46020     // interface method
46021     success : function(response){
46022
46023     },
46024
46025     // interface method
46026     handleResponse : function(response){
46027
46028     },
46029
46030     // default connection failure
46031     failure : function(response){
46032         
46033         this.response = response;
46034         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46035         this.form.afterAction(this, false);
46036     },
46037
46038     processResponse : function(response){
46039         this.response = response;
46040         if(!response.responseText){
46041             return true;
46042         }
46043         this.result = this.handleResponse(response);
46044         return this.result;
46045     },
46046
46047     // utility functions used internally
46048     getUrl : function(appendParams){
46049         var url = this.options.url || this.form.url || this.form.el.dom.action;
46050         if(appendParams){
46051             var p = this.getParams();
46052             if(p){
46053                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46054             }
46055         }
46056         return url;
46057     },
46058
46059     getMethod : function(){
46060         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46061     },
46062
46063     getParams : function(){
46064         var bp = this.form.baseParams;
46065         var p = this.options.params;
46066         if(p){
46067             if(typeof p == "object"){
46068                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46069             }else if(typeof p == 'string' && bp){
46070                 p += '&' + Roo.urlEncode(bp);
46071             }
46072         }else if(bp){
46073             p = Roo.urlEncode(bp);
46074         }
46075         return p;
46076     },
46077
46078     createCallback : function(){
46079         return {
46080             success: this.success,
46081             failure: this.failure,
46082             scope: this,
46083             timeout: (this.form.timeout*1000),
46084             upload: this.form.fileUpload ? this.success : undefined
46085         };
46086     }
46087 };
46088
46089 Roo.form.Action.Submit = function(form, options){
46090     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46091 };
46092
46093 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46094     type : 'submit',
46095
46096     haveProgress : false,
46097     uploadComplete : false,
46098     
46099     // uploadProgress indicator.
46100     uploadProgress : function()
46101     {
46102         if (!this.form.progressUrl) {
46103             return;
46104         }
46105         
46106         if (!this.haveProgress) {
46107             Roo.MessageBox.progress("Uploading", "Uploading");
46108         }
46109         if (this.uploadComplete) {
46110            Roo.MessageBox.hide();
46111            return;
46112         }
46113         
46114         this.haveProgress = true;
46115    
46116         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46117         
46118         var c = new Roo.data.Connection();
46119         c.request({
46120             url : this.form.progressUrl,
46121             params: {
46122                 id : uid
46123             },
46124             method: 'GET',
46125             success : function(req){
46126                //console.log(data);
46127                 var rdata = false;
46128                 var edata;
46129                 try  {
46130                    rdata = Roo.decode(req.responseText)
46131                 } catch (e) {
46132                     Roo.log("Invalid data from server..");
46133                     Roo.log(edata);
46134                     return;
46135                 }
46136                 if (!rdata || !rdata.success) {
46137                     Roo.log(rdata);
46138                     Roo.MessageBox.alert(Roo.encode(rdata));
46139                     return;
46140                 }
46141                 var data = rdata.data;
46142                 
46143                 if (this.uploadComplete) {
46144                    Roo.MessageBox.hide();
46145                    return;
46146                 }
46147                    
46148                 if (data){
46149                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46150                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46151                     );
46152                 }
46153                 this.uploadProgress.defer(2000,this);
46154             },
46155        
46156             failure: function(data) {
46157                 Roo.log('progress url failed ');
46158                 Roo.log(data);
46159             },
46160             scope : this
46161         });
46162            
46163     },
46164     
46165     
46166     run : function()
46167     {
46168         // run get Values on the form, so it syncs any secondary forms.
46169         this.form.getValues();
46170         
46171         var o = this.options;
46172         var method = this.getMethod();
46173         var isPost = method == 'POST';
46174         if(o.clientValidation === false || this.form.isValid()){
46175             
46176             if (this.form.progressUrl) {
46177                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46178                     (new Date() * 1) + '' + Math.random());
46179                     
46180             } 
46181             
46182             
46183             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46184                 form:this.form.el.dom,
46185                 url:this.getUrl(!isPost),
46186                 method: method,
46187                 params:isPost ? this.getParams() : null,
46188                 isUpload: this.form.fileUpload
46189             }));
46190             
46191             this.uploadProgress();
46192
46193         }else if (o.clientValidation !== false){ // client validation failed
46194             this.failureType = Roo.form.Action.CLIENT_INVALID;
46195             this.form.afterAction(this, false);
46196         }
46197     },
46198
46199     success : function(response)
46200     {
46201         this.uploadComplete= true;
46202         if (this.haveProgress) {
46203             Roo.MessageBox.hide();
46204         }
46205         
46206         
46207         var result = this.processResponse(response);
46208         if(result === true || result.success){
46209             this.form.afterAction(this, true);
46210             return;
46211         }
46212         if(result.errors){
46213             this.form.markInvalid(result.errors);
46214             this.failureType = Roo.form.Action.SERVER_INVALID;
46215         }
46216         this.form.afterAction(this, false);
46217     },
46218     failure : function(response)
46219     {
46220         this.uploadComplete= true;
46221         if (this.haveProgress) {
46222             Roo.MessageBox.hide();
46223         }
46224         
46225         this.response = response;
46226         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46227         this.form.afterAction(this, false);
46228     },
46229     
46230     handleResponse : function(response){
46231         if(this.form.errorReader){
46232             var rs = this.form.errorReader.read(response);
46233             var errors = [];
46234             if(rs.records){
46235                 for(var i = 0, len = rs.records.length; i < len; i++) {
46236                     var r = rs.records[i];
46237                     errors[i] = r.data;
46238                 }
46239             }
46240             if(errors.length < 1){
46241                 errors = null;
46242             }
46243             return {
46244                 success : rs.success,
46245                 errors : errors
46246             };
46247         }
46248         var ret = false;
46249         try {
46250             ret = Roo.decode(response.responseText);
46251         } catch (e) {
46252             ret = {
46253                 success: false,
46254                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46255                 errors : []
46256             };
46257         }
46258         return ret;
46259         
46260     }
46261 });
46262
46263
46264 Roo.form.Action.Load = function(form, options){
46265     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46266     this.reader = this.form.reader;
46267 };
46268
46269 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46270     type : 'load',
46271
46272     run : function(){
46273         
46274         Roo.Ajax.request(Roo.apply(
46275                 this.createCallback(), {
46276                     method:this.getMethod(),
46277                     url:this.getUrl(false),
46278                     params:this.getParams()
46279         }));
46280     },
46281
46282     success : function(response){
46283         
46284         var result = this.processResponse(response);
46285         if(result === true || !result.success || !result.data){
46286             this.failureType = Roo.form.Action.LOAD_FAILURE;
46287             this.form.afterAction(this, false);
46288             return;
46289         }
46290         this.form.clearInvalid();
46291         this.form.setValues(result.data);
46292         this.form.afterAction(this, true);
46293     },
46294
46295     handleResponse : function(response){
46296         if(this.form.reader){
46297             var rs = this.form.reader.read(response);
46298             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46299             return {
46300                 success : rs.success,
46301                 data : data
46302             };
46303         }
46304         return Roo.decode(response.responseText);
46305     }
46306 });
46307
46308 Roo.form.Action.ACTION_TYPES = {
46309     'load' : Roo.form.Action.Load,
46310     'submit' : Roo.form.Action.Submit
46311 };/*
46312  * Based on:
46313  * Ext JS Library 1.1.1
46314  * Copyright(c) 2006-2007, Ext JS, LLC.
46315  *
46316  * Originally Released Under LGPL - original licence link has changed is not relivant.
46317  *
46318  * Fork - LGPL
46319  * <script type="text/javascript">
46320  */
46321  
46322 /**
46323  * @class Roo.form.Layout
46324  * @extends Roo.Component
46325  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46326  * @constructor
46327  * @param {Object} config Configuration options
46328  */
46329 Roo.form.Layout = function(config){
46330     var xitems = [];
46331     if (config.items) {
46332         xitems = config.items;
46333         delete config.items;
46334     }
46335     Roo.form.Layout.superclass.constructor.call(this, config);
46336     this.stack = [];
46337     Roo.each(xitems, this.addxtype, this);
46338      
46339 };
46340
46341 Roo.extend(Roo.form.Layout, Roo.Component, {
46342     /**
46343      * @cfg {String/Object} autoCreate
46344      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46345      */
46346     /**
46347      * @cfg {String/Object/Function} style
46348      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46349      * a function which returns such a specification.
46350      */
46351     /**
46352      * @cfg {String} labelAlign
46353      * Valid values are "left," "top" and "right" (defaults to "left")
46354      */
46355     /**
46356      * @cfg {Number} labelWidth
46357      * Fixed width in pixels of all field labels (defaults to undefined)
46358      */
46359     /**
46360      * @cfg {Boolean} clear
46361      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46362      */
46363     clear : true,
46364     /**
46365      * @cfg {String} labelSeparator
46366      * The separator to use after field labels (defaults to ':')
46367      */
46368     labelSeparator : ':',
46369     /**
46370      * @cfg {Boolean} hideLabels
46371      * True to suppress the display of field labels in this layout (defaults to false)
46372      */
46373     hideLabels : false,
46374
46375     // private
46376     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46377     
46378     isLayout : true,
46379     
46380     // private
46381     onRender : function(ct, position){
46382         if(this.el){ // from markup
46383             this.el = Roo.get(this.el);
46384         }else {  // generate
46385             var cfg = this.getAutoCreate();
46386             this.el = ct.createChild(cfg, position);
46387         }
46388         if(this.style){
46389             this.el.applyStyles(this.style);
46390         }
46391         if(this.labelAlign){
46392             this.el.addClass('x-form-label-'+this.labelAlign);
46393         }
46394         if(this.hideLabels){
46395             this.labelStyle = "display:none";
46396             this.elementStyle = "padding-left:0;";
46397         }else{
46398             if(typeof this.labelWidth == 'number'){
46399                 this.labelStyle = "width:"+this.labelWidth+"px;";
46400                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46401             }
46402             if(this.labelAlign == 'top'){
46403                 this.labelStyle = "width:auto;";
46404                 this.elementStyle = "padding-left:0;";
46405             }
46406         }
46407         var stack = this.stack;
46408         var slen = stack.length;
46409         if(slen > 0){
46410             if(!this.fieldTpl){
46411                 var t = new Roo.Template(
46412                     '<div class="x-form-item {5}">',
46413                         '<label for="{0}" style="{2}">{1}{4}</label>',
46414                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46415                         '</div>',
46416                     '</div><div class="x-form-clear-left"></div>'
46417                 );
46418                 t.disableFormats = true;
46419                 t.compile();
46420                 Roo.form.Layout.prototype.fieldTpl = t;
46421             }
46422             for(var i = 0; i < slen; i++) {
46423                 if(stack[i].isFormField){
46424                     this.renderField(stack[i]);
46425                 }else{
46426                     this.renderComponent(stack[i]);
46427                 }
46428             }
46429         }
46430         if(this.clear){
46431             this.el.createChild({cls:'x-form-clear'});
46432         }
46433     },
46434
46435     // private
46436     renderField : function(f){
46437         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46438                f.id, //0
46439                f.fieldLabel, //1
46440                f.labelStyle||this.labelStyle||'', //2
46441                this.elementStyle||'', //3
46442                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46443                f.itemCls||this.itemCls||''  //5
46444        ], true).getPrevSibling());
46445     },
46446
46447     // private
46448     renderComponent : function(c){
46449         c.render(c.isLayout ? this.el : this.el.createChild());    
46450     },
46451     /**
46452      * Adds a object form elements (using the xtype property as the factory method.)
46453      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46454      * @param {Object} config 
46455      */
46456     addxtype : function(o)
46457     {
46458         // create the lement.
46459         o.form = this.form;
46460         var fe = Roo.factory(o, Roo.form);
46461         this.form.allItems.push(fe);
46462         this.stack.push(fe);
46463         
46464         if (fe.isFormField) {
46465             this.form.items.add(fe);
46466         }
46467          
46468         return fe;
46469     }
46470 });
46471
46472 /**
46473  * @class Roo.form.Column
46474  * @extends Roo.form.Layout
46475  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46476  * @constructor
46477  * @param {Object} config Configuration options
46478  */
46479 Roo.form.Column = function(config){
46480     Roo.form.Column.superclass.constructor.call(this, config);
46481 };
46482
46483 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46484     /**
46485      * @cfg {Number/String} width
46486      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46487      */
46488     /**
46489      * @cfg {String/Object} autoCreate
46490      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46491      */
46492
46493     // private
46494     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46495
46496     // private
46497     onRender : function(ct, position){
46498         Roo.form.Column.superclass.onRender.call(this, ct, position);
46499         if(this.width){
46500             this.el.setWidth(this.width);
46501         }
46502     }
46503 });
46504
46505
46506 /**
46507  * @class Roo.form.Row
46508  * @extends Roo.form.Layout
46509  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46510  * @constructor
46511  * @param {Object} config Configuration options
46512  */
46513
46514  
46515 Roo.form.Row = function(config){
46516     Roo.form.Row.superclass.constructor.call(this, config);
46517 };
46518  
46519 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46520       /**
46521      * @cfg {Number/String} width
46522      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46523      */
46524     /**
46525      * @cfg {Number/String} height
46526      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46527      */
46528     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46529     
46530     padWidth : 20,
46531     // private
46532     onRender : function(ct, position){
46533         //console.log('row render');
46534         if(!this.rowTpl){
46535             var t = new Roo.Template(
46536                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46537                     '<label for="{0}" style="{2}">{1}{4}</label>',
46538                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46539                     '</div>',
46540                 '</div>'
46541             );
46542             t.disableFormats = true;
46543             t.compile();
46544             Roo.form.Layout.prototype.rowTpl = t;
46545         }
46546         this.fieldTpl = this.rowTpl;
46547         
46548         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46549         var labelWidth = 100;
46550         
46551         if ((this.labelAlign != 'top')) {
46552             if (typeof this.labelWidth == 'number') {
46553                 labelWidth = this.labelWidth
46554             }
46555             this.padWidth =  20 + labelWidth;
46556             
46557         }
46558         
46559         Roo.form.Column.superclass.onRender.call(this, ct, position);
46560         if(this.width){
46561             this.el.setWidth(this.width);
46562         }
46563         if(this.height){
46564             this.el.setHeight(this.height);
46565         }
46566     },
46567     
46568     // private
46569     renderField : function(f){
46570         f.fieldEl = this.fieldTpl.append(this.el, [
46571                f.id, f.fieldLabel,
46572                f.labelStyle||this.labelStyle||'',
46573                this.elementStyle||'',
46574                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46575                f.itemCls||this.itemCls||'',
46576                f.width ? f.width + this.padWidth : 160 + this.padWidth
46577        ],true);
46578     }
46579 });
46580  
46581
46582 /**
46583  * @class Roo.form.FieldSet
46584  * @extends Roo.form.Layout
46585  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46586  * @constructor
46587  * @param {Object} config Configuration options
46588  */
46589 Roo.form.FieldSet = function(config){
46590     Roo.form.FieldSet.superclass.constructor.call(this, config);
46591 };
46592
46593 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46594     /**
46595      * @cfg {String} legend
46596      * The text to display as the legend for the FieldSet (defaults to '')
46597      */
46598     /**
46599      * @cfg {String/Object} autoCreate
46600      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46601      */
46602
46603     // private
46604     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46605
46606     // private
46607     onRender : function(ct, position){
46608         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46609         if(this.legend){
46610             this.setLegend(this.legend);
46611         }
46612     },
46613
46614     // private
46615     setLegend : function(text){
46616         if(this.rendered){
46617             this.el.child('legend').update(text);
46618         }
46619     }
46620 });/*
46621  * Based on:
46622  * Ext JS Library 1.1.1
46623  * Copyright(c) 2006-2007, Ext JS, LLC.
46624  *
46625  * Originally Released Under LGPL - original licence link has changed is not relivant.
46626  *
46627  * Fork - LGPL
46628  * <script type="text/javascript">
46629  */
46630 /**
46631  * @class Roo.form.VTypes
46632  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46633  * @singleton
46634  */
46635 Roo.form.VTypes = function(){
46636     // closure these in so they are only created once.
46637     var alpha = /^[a-zA-Z_]+$/;
46638     var alphanum = /^[a-zA-Z0-9_]+$/;
46639     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46640     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46641
46642     // All these messages and functions are configurable
46643     return {
46644         /**
46645          * The function used to validate email addresses
46646          * @param {String} value The email address
46647          */
46648         'email' : function(v){
46649             return email.test(v);
46650         },
46651         /**
46652          * The error text to display when the email validation function returns false
46653          * @type String
46654          */
46655         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46656         /**
46657          * The keystroke filter mask to be applied on email input
46658          * @type RegExp
46659          */
46660         'emailMask' : /[a-z0-9_\.\-@]/i,
46661
46662         /**
46663          * The function used to validate URLs
46664          * @param {String} value The URL
46665          */
46666         'url' : function(v){
46667             return url.test(v);
46668         },
46669         /**
46670          * The error text to display when the url validation function returns false
46671          * @type String
46672          */
46673         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46674         
46675         /**
46676          * The function used to validate alpha values
46677          * @param {String} value The value
46678          */
46679         'alpha' : function(v){
46680             return alpha.test(v);
46681         },
46682         /**
46683          * The error text to display when the alpha validation function returns false
46684          * @type String
46685          */
46686         'alphaText' : 'This field should only contain letters and _',
46687         /**
46688          * The keystroke filter mask to be applied on alpha input
46689          * @type RegExp
46690          */
46691         'alphaMask' : /[a-z_]/i,
46692
46693         /**
46694          * The function used to validate alphanumeric values
46695          * @param {String} value The value
46696          */
46697         'alphanum' : function(v){
46698             return alphanum.test(v);
46699         },
46700         /**
46701          * The error text to display when the alphanumeric validation function returns false
46702          * @type String
46703          */
46704         'alphanumText' : 'This field should only contain letters, numbers and _',
46705         /**
46706          * The keystroke filter mask to be applied on alphanumeric input
46707          * @type RegExp
46708          */
46709         'alphanumMask' : /[a-z0-9_]/i
46710     };
46711 }();//<script type="text/javascript">
46712
46713 /**
46714  * @class Roo.form.FCKeditor
46715  * @extends Roo.form.TextArea
46716  * Wrapper around the FCKEditor http://www.fckeditor.net
46717  * @constructor
46718  * Creates a new FCKeditor
46719  * @param {Object} config Configuration options
46720  */
46721 Roo.form.FCKeditor = function(config){
46722     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46723     this.addEvents({
46724          /**
46725          * @event editorinit
46726          * Fired when the editor is initialized - you can add extra handlers here..
46727          * @param {FCKeditor} this
46728          * @param {Object} the FCK object.
46729          */
46730         editorinit : true
46731     });
46732     
46733     
46734 };
46735 Roo.form.FCKeditor.editors = { };
46736 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46737 {
46738     //defaultAutoCreate : {
46739     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46740     //},
46741     // private
46742     /**
46743      * @cfg {Object} fck options - see fck manual for details.
46744      */
46745     fckconfig : false,
46746     
46747     /**
46748      * @cfg {Object} fck toolbar set (Basic or Default)
46749      */
46750     toolbarSet : 'Basic',
46751     /**
46752      * @cfg {Object} fck BasePath
46753      */ 
46754     basePath : '/fckeditor/',
46755     
46756     
46757     frame : false,
46758     
46759     value : '',
46760     
46761    
46762     onRender : function(ct, position)
46763     {
46764         if(!this.el){
46765             this.defaultAutoCreate = {
46766                 tag: "textarea",
46767                 style:"width:300px;height:60px;",
46768                 autocomplete: "new-password"
46769             };
46770         }
46771         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46772         /*
46773         if(this.grow){
46774             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46775             if(this.preventScrollbars){
46776                 this.el.setStyle("overflow", "hidden");
46777             }
46778             this.el.setHeight(this.growMin);
46779         }
46780         */
46781         //console.log('onrender' + this.getId() );
46782         Roo.form.FCKeditor.editors[this.getId()] = this;
46783          
46784
46785         this.replaceTextarea() ;
46786         
46787     },
46788     
46789     getEditor : function() {
46790         return this.fckEditor;
46791     },
46792     /**
46793      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46794      * @param {Mixed} value The value to set
46795      */
46796     
46797     
46798     setValue : function(value)
46799     {
46800         //console.log('setValue: ' + value);
46801         
46802         if(typeof(value) == 'undefined') { // not sure why this is happending...
46803             return;
46804         }
46805         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46806         
46807         //if(!this.el || !this.getEditor()) {
46808         //    this.value = value;
46809             //this.setValue.defer(100,this,[value]);    
46810         //    return;
46811         //} 
46812         
46813         if(!this.getEditor()) {
46814             return;
46815         }
46816         
46817         this.getEditor().SetData(value);
46818         
46819         //
46820
46821     },
46822
46823     /**
46824      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46825      * @return {Mixed} value The field value
46826      */
46827     getValue : function()
46828     {
46829         
46830         if (this.frame && this.frame.dom.style.display == 'none') {
46831             return Roo.form.FCKeditor.superclass.getValue.call(this);
46832         }
46833         
46834         if(!this.el || !this.getEditor()) {
46835            
46836            // this.getValue.defer(100,this); 
46837             return this.value;
46838         }
46839        
46840         
46841         var value=this.getEditor().GetData();
46842         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46843         return Roo.form.FCKeditor.superclass.getValue.call(this);
46844         
46845
46846     },
46847
46848     /**
46849      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46850      * @return {Mixed} value The field value
46851      */
46852     getRawValue : function()
46853     {
46854         if (this.frame && this.frame.dom.style.display == 'none') {
46855             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46856         }
46857         
46858         if(!this.el || !this.getEditor()) {
46859             //this.getRawValue.defer(100,this); 
46860             return this.value;
46861             return;
46862         }
46863         
46864         
46865         
46866         var value=this.getEditor().GetData();
46867         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46868         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46869          
46870     },
46871     
46872     setSize : function(w,h) {
46873         
46874         
46875         
46876         //if (this.frame && this.frame.dom.style.display == 'none') {
46877         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46878         //    return;
46879         //}
46880         //if(!this.el || !this.getEditor()) {
46881         //    this.setSize.defer(100,this, [w,h]); 
46882         //    return;
46883         //}
46884         
46885         
46886         
46887         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46888         
46889         this.frame.dom.setAttribute('width', w);
46890         this.frame.dom.setAttribute('height', h);
46891         this.frame.setSize(w,h);
46892         
46893     },
46894     
46895     toggleSourceEdit : function(value) {
46896         
46897       
46898          
46899         this.el.dom.style.display = value ? '' : 'none';
46900         this.frame.dom.style.display = value ?  'none' : '';
46901         
46902     },
46903     
46904     
46905     focus: function(tag)
46906     {
46907         if (this.frame.dom.style.display == 'none') {
46908             return Roo.form.FCKeditor.superclass.focus.call(this);
46909         }
46910         if(!this.el || !this.getEditor()) {
46911             this.focus.defer(100,this, [tag]); 
46912             return;
46913         }
46914         
46915         
46916         
46917         
46918         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46919         this.getEditor().Focus();
46920         if (tgs.length) {
46921             if (!this.getEditor().Selection.GetSelection()) {
46922                 this.focus.defer(100,this, [tag]); 
46923                 return;
46924             }
46925             
46926             
46927             var r = this.getEditor().EditorDocument.createRange();
46928             r.setStart(tgs[0],0);
46929             r.setEnd(tgs[0],0);
46930             this.getEditor().Selection.GetSelection().removeAllRanges();
46931             this.getEditor().Selection.GetSelection().addRange(r);
46932             this.getEditor().Focus();
46933         }
46934         
46935     },
46936     
46937     
46938     
46939     replaceTextarea : function()
46940     {
46941         if ( document.getElementById( this.getId() + '___Frame' ) )
46942             return ;
46943         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46944         //{
46945             // We must check the elements firstly using the Id and then the name.
46946         var oTextarea = document.getElementById( this.getId() );
46947         
46948         var colElementsByName = document.getElementsByName( this.getId() ) ;
46949          
46950         oTextarea.style.display = 'none' ;
46951
46952         if ( oTextarea.tabIndex ) {            
46953             this.TabIndex = oTextarea.tabIndex ;
46954         }
46955         
46956         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46957         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46958         this.frame = Roo.get(this.getId() + '___Frame')
46959     },
46960     
46961     _getConfigHtml : function()
46962     {
46963         var sConfig = '' ;
46964
46965         for ( var o in this.fckconfig ) {
46966             sConfig += sConfig.length > 0  ? '&amp;' : '';
46967             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46968         }
46969
46970         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46971     },
46972     
46973     
46974     _getIFrameHtml : function()
46975     {
46976         var sFile = 'fckeditor.html' ;
46977         /* no idea what this is about..
46978         try
46979         {
46980             if ( (/fcksource=true/i).test( window.top.location.search ) )
46981                 sFile = 'fckeditor.original.html' ;
46982         }
46983         catch (e) { 
46984         */
46985
46986         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46987         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46988         
46989         
46990         var html = '<iframe id="' + this.getId() +
46991             '___Frame" src="' + sLink +
46992             '" width="' + this.width +
46993             '" height="' + this.height + '"' +
46994             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46995             ' frameborder="0" scrolling="no"></iframe>' ;
46996
46997         return html ;
46998     },
46999     
47000     _insertHtmlBefore : function( html, element )
47001     {
47002         if ( element.insertAdjacentHTML )       {
47003             // IE
47004             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47005         } else { // Gecko
47006             var oRange = document.createRange() ;
47007             oRange.setStartBefore( element ) ;
47008             var oFragment = oRange.createContextualFragment( html );
47009             element.parentNode.insertBefore( oFragment, element ) ;
47010         }
47011     }
47012     
47013     
47014   
47015     
47016     
47017     
47018     
47019
47020 });
47021
47022 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47023
47024 function FCKeditor_OnComplete(editorInstance){
47025     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47026     f.fckEditor = editorInstance;
47027     //console.log("loaded");
47028     f.fireEvent('editorinit', f, editorInstance);
47029
47030   
47031
47032  
47033
47034
47035
47036
47037
47038
47039
47040
47041
47042
47043
47044
47045
47046
47047
47048 //<script type="text/javascript">
47049 /**
47050  * @class Roo.form.GridField
47051  * @extends Roo.form.Field
47052  * Embed a grid (or editable grid into a form)
47053  * STATUS ALPHA
47054  * 
47055  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47056  * it needs 
47057  * xgrid.store = Roo.data.Store
47058  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47059  * xgrid.store.reader = Roo.data.JsonReader 
47060  * 
47061  * 
47062  * @constructor
47063  * Creates a new GridField
47064  * @param {Object} config Configuration options
47065  */
47066 Roo.form.GridField = function(config){
47067     Roo.form.GridField.superclass.constructor.call(this, config);
47068      
47069 };
47070
47071 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47072     /**
47073      * @cfg {Number} width  - used to restrict width of grid..
47074      */
47075     width : 100,
47076     /**
47077      * @cfg {Number} height - used to restrict height of grid..
47078      */
47079     height : 50,
47080      /**
47081      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47082          * 
47083          *}
47084      */
47085     xgrid : false, 
47086     /**
47087      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47088      * {tag: "input", type: "checkbox", autocomplete: "off"})
47089      */
47090    // defaultAutoCreate : { tag: 'div' },
47091     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47092     /**
47093      * @cfg {String} addTitle Text to include for adding a title.
47094      */
47095     addTitle : false,
47096     //
47097     onResize : function(){
47098         Roo.form.Field.superclass.onResize.apply(this, arguments);
47099     },
47100
47101     initEvents : function(){
47102         // Roo.form.Checkbox.superclass.initEvents.call(this);
47103         // has no events...
47104        
47105     },
47106
47107
47108     getResizeEl : function(){
47109         return this.wrap;
47110     },
47111
47112     getPositionEl : function(){
47113         return this.wrap;
47114     },
47115
47116     // private
47117     onRender : function(ct, position){
47118         
47119         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47120         var style = this.style;
47121         delete this.style;
47122         
47123         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47124         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47125         this.viewEl = this.wrap.createChild({ tag: 'div' });
47126         if (style) {
47127             this.viewEl.applyStyles(style);
47128         }
47129         if (this.width) {
47130             this.viewEl.setWidth(this.width);
47131         }
47132         if (this.height) {
47133             this.viewEl.setHeight(this.height);
47134         }
47135         //if(this.inputValue !== undefined){
47136         //this.setValue(this.value);
47137         
47138         
47139         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47140         
47141         
47142         this.grid.render();
47143         this.grid.getDataSource().on('remove', this.refreshValue, this);
47144         this.grid.getDataSource().on('update', this.refreshValue, this);
47145         this.grid.on('afteredit', this.refreshValue, this);
47146  
47147     },
47148      
47149     
47150     /**
47151      * Sets the value of the item. 
47152      * @param {String} either an object  or a string..
47153      */
47154     setValue : function(v){
47155         //this.value = v;
47156         v = v || []; // empty set..
47157         // this does not seem smart - it really only affects memoryproxy grids..
47158         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47159             var ds = this.grid.getDataSource();
47160             // assumes a json reader..
47161             var data = {}
47162             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47163             ds.loadData( data);
47164         }
47165         // clear selection so it does not get stale.
47166         if (this.grid.sm) { 
47167             this.grid.sm.clearSelections();
47168         }
47169         
47170         Roo.form.GridField.superclass.setValue.call(this, v);
47171         this.refreshValue();
47172         // should load data in the grid really....
47173     },
47174     
47175     // private
47176     refreshValue: function() {
47177          var val = [];
47178         this.grid.getDataSource().each(function(r) {
47179             val.push(r.data);
47180         });
47181         this.el.dom.value = Roo.encode(val);
47182     }
47183     
47184      
47185     
47186     
47187 });/*
47188  * Based on:
47189  * Ext JS Library 1.1.1
47190  * Copyright(c) 2006-2007, Ext JS, LLC.
47191  *
47192  * Originally Released Under LGPL - original licence link has changed is not relivant.
47193  *
47194  * Fork - LGPL
47195  * <script type="text/javascript">
47196  */
47197 /**
47198  * @class Roo.form.DisplayField
47199  * @extends Roo.form.Field
47200  * A generic Field to display non-editable data.
47201  * @constructor
47202  * Creates a new Display Field item.
47203  * @param {Object} config Configuration options
47204  */
47205 Roo.form.DisplayField = function(config){
47206     Roo.form.DisplayField.superclass.constructor.call(this, config);
47207     
47208 };
47209
47210 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47211     inputType:      'hidden',
47212     allowBlank:     true,
47213     readOnly:         true,
47214     
47215  
47216     /**
47217      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47218      */
47219     focusClass : undefined,
47220     /**
47221      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47222      */
47223     fieldClass: 'x-form-field',
47224     
47225      /**
47226      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47227      */
47228     valueRenderer: undefined,
47229     
47230     width: 100,
47231     /**
47232      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47233      * {tag: "input", type: "checkbox", autocomplete: "off"})
47234      */
47235      
47236  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47237
47238     onResize : function(){
47239         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47240         
47241     },
47242
47243     initEvents : function(){
47244         // Roo.form.Checkbox.superclass.initEvents.call(this);
47245         // has no events...
47246        
47247     },
47248
47249
47250     getResizeEl : function(){
47251         return this.wrap;
47252     },
47253
47254     getPositionEl : function(){
47255         return this.wrap;
47256     },
47257
47258     // private
47259     onRender : function(ct, position){
47260         
47261         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47262         //if(this.inputValue !== undefined){
47263         this.wrap = this.el.wrap();
47264         
47265         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47266         
47267         if (this.bodyStyle) {
47268             this.viewEl.applyStyles(this.bodyStyle);
47269         }
47270         //this.viewEl.setStyle('padding', '2px');
47271         
47272         this.setValue(this.value);
47273         
47274     },
47275 /*
47276     // private
47277     initValue : Roo.emptyFn,
47278
47279   */
47280
47281         // private
47282     onClick : function(){
47283         
47284     },
47285
47286     /**
47287      * Sets the checked state of the checkbox.
47288      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47289      */
47290     setValue : function(v){
47291         this.value = v;
47292         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47293         // this might be called before we have a dom element..
47294         if (!this.viewEl) {
47295             return;
47296         }
47297         this.viewEl.dom.innerHTML = html;
47298         Roo.form.DisplayField.superclass.setValue.call(this, v);
47299
47300     }
47301 });/*
47302  * 
47303  * Licence- LGPL
47304  * 
47305  */
47306
47307 /**
47308  * @class Roo.form.DayPicker
47309  * @extends Roo.form.Field
47310  * A Day picker show [M] [T] [W] ....
47311  * @constructor
47312  * Creates a new Day Picker
47313  * @param {Object} config Configuration options
47314  */
47315 Roo.form.DayPicker= function(config){
47316     Roo.form.DayPicker.superclass.constructor.call(this, config);
47317      
47318 };
47319
47320 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47321     /**
47322      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47323      */
47324     focusClass : undefined,
47325     /**
47326      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47327      */
47328     fieldClass: "x-form-field",
47329    
47330     /**
47331      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47332      * {tag: "input", type: "checkbox", autocomplete: "off"})
47333      */
47334     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47335     
47336    
47337     actionMode : 'viewEl', 
47338     //
47339     // private
47340  
47341     inputType : 'hidden',
47342     
47343      
47344     inputElement: false, // real input element?
47345     basedOn: false, // ????
47346     
47347     isFormField: true, // not sure where this is needed!!!!
47348
47349     onResize : function(){
47350         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47351         if(!this.boxLabel){
47352             this.el.alignTo(this.wrap, 'c-c');
47353         }
47354     },
47355
47356     initEvents : function(){
47357         Roo.form.Checkbox.superclass.initEvents.call(this);
47358         this.el.on("click", this.onClick,  this);
47359         this.el.on("change", this.onClick,  this);
47360     },
47361
47362
47363     getResizeEl : function(){
47364         return this.wrap;
47365     },
47366
47367     getPositionEl : function(){
47368         return this.wrap;
47369     },
47370
47371     
47372     // private
47373     onRender : function(ct, position){
47374         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47375        
47376         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47377         
47378         var r1 = '<table><tr>';
47379         var r2 = '<tr class="x-form-daypick-icons">';
47380         for (var i=0; i < 7; i++) {
47381             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47382             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47383         }
47384         
47385         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47386         viewEl.select('img').on('click', this.onClick, this);
47387         this.viewEl = viewEl;   
47388         
47389         
47390         // this will not work on Chrome!!!
47391         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47392         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47393         
47394         
47395           
47396
47397     },
47398
47399     // private
47400     initValue : Roo.emptyFn,
47401
47402     /**
47403      * Returns the checked state of the checkbox.
47404      * @return {Boolean} True if checked, else false
47405      */
47406     getValue : function(){
47407         return this.el.dom.value;
47408         
47409     },
47410
47411         // private
47412     onClick : function(e){ 
47413         //this.setChecked(!this.checked);
47414         Roo.get(e.target).toggleClass('x-menu-item-checked');
47415         this.refreshValue();
47416         //if(this.el.dom.checked != this.checked){
47417         //    this.setValue(this.el.dom.checked);
47418        // }
47419     },
47420     
47421     // private
47422     refreshValue : function()
47423     {
47424         var val = '';
47425         this.viewEl.select('img',true).each(function(e,i,n)  {
47426             val += e.is(".x-menu-item-checked") ? String(n) : '';
47427         });
47428         this.setValue(val, true);
47429     },
47430
47431     /**
47432      * Sets the checked state of the checkbox.
47433      * On is always based on a string comparison between inputValue and the param.
47434      * @param {Boolean/String} value - the value to set 
47435      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47436      */
47437     setValue : function(v,suppressEvent){
47438         if (!this.el.dom) {
47439             return;
47440         }
47441         var old = this.el.dom.value ;
47442         this.el.dom.value = v;
47443         if (suppressEvent) {
47444             return ;
47445         }
47446          
47447         // update display..
47448         this.viewEl.select('img',true).each(function(e,i,n)  {
47449             
47450             var on = e.is(".x-menu-item-checked");
47451             var newv = v.indexOf(String(n)) > -1;
47452             if (on != newv) {
47453                 e.toggleClass('x-menu-item-checked');
47454             }
47455             
47456         });
47457         
47458         
47459         this.fireEvent('change', this, v, old);
47460         
47461         
47462     },
47463    
47464     // handle setting of hidden value by some other method!!?!?
47465     setFromHidden: function()
47466     {
47467         if(!this.el){
47468             return;
47469         }
47470         //console.log("SET FROM HIDDEN");
47471         //alert('setFrom hidden');
47472         this.setValue(this.el.dom.value);
47473     },
47474     
47475     onDestroy : function()
47476     {
47477         if(this.viewEl){
47478             Roo.get(this.viewEl).remove();
47479         }
47480          
47481         Roo.form.DayPicker.superclass.onDestroy.call(this);
47482     }
47483
47484 });/*
47485  * RooJS Library 1.1.1
47486  * Copyright(c) 2008-2011  Alan Knowles
47487  *
47488  * License - LGPL
47489  */
47490  
47491
47492 /**
47493  * @class Roo.form.ComboCheck
47494  * @extends Roo.form.ComboBox
47495  * A combobox for multiple select items.
47496  *
47497  * FIXME - could do with a reset button..
47498  * 
47499  * @constructor
47500  * Create a new ComboCheck
47501  * @param {Object} config Configuration options
47502  */
47503 Roo.form.ComboCheck = function(config){
47504     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47505     // should verify some data...
47506     // like
47507     // hiddenName = required..
47508     // displayField = required
47509     // valudField == required
47510     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47511     var _t = this;
47512     Roo.each(req, function(e) {
47513         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47514             throw "Roo.form.ComboCheck : missing value for: " + e;
47515         }
47516     });
47517     
47518     
47519 };
47520
47521 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47522      
47523      
47524     editable : false,
47525      
47526     selectedClass: 'x-menu-item-checked', 
47527     
47528     // private
47529     onRender : function(ct, position){
47530         var _t = this;
47531         
47532         
47533         
47534         if(!this.tpl){
47535             var cls = 'x-combo-list';
47536
47537             
47538             this.tpl =  new Roo.Template({
47539                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47540                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47541                    '<span>{' + this.displayField + '}</span>' +
47542                     '</div>' 
47543                 
47544             });
47545         }
47546  
47547         
47548         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47549         this.view.singleSelect = false;
47550         this.view.multiSelect = true;
47551         this.view.toggleSelect = true;
47552         this.pageTb.add(new Roo.Toolbar.Fill(), {
47553             
47554             text: 'Done',
47555             handler: function()
47556             {
47557                 _t.collapse();
47558             }
47559         });
47560     },
47561     
47562     onViewOver : function(e, t){
47563         // do nothing...
47564         return;
47565         
47566     },
47567     
47568     onViewClick : function(doFocus,index){
47569         return;
47570         
47571     },
47572     select: function () {
47573         //Roo.log("SELECT CALLED");
47574     },
47575      
47576     selectByValue : function(xv, scrollIntoView){
47577         var ar = this.getValueArray();
47578         var sels = [];
47579         
47580         Roo.each(ar, function(v) {
47581             if(v === undefined || v === null){
47582                 return;
47583             }
47584             var r = this.findRecord(this.valueField, v);
47585             if(r){
47586                 sels.push(this.store.indexOf(r))
47587                 
47588             }
47589         },this);
47590         this.view.select(sels);
47591         return false;
47592     },
47593     
47594     
47595     
47596     onSelect : function(record, index){
47597        // Roo.log("onselect Called");
47598        // this is only called by the clear button now..
47599         this.view.clearSelections();
47600         this.setValue('[]');
47601         if (this.value != this.valueBefore) {
47602             this.fireEvent('change', this, this.value, this.valueBefore);
47603             this.valueBefore = this.value;
47604         }
47605     },
47606     getValueArray : function()
47607     {
47608         var ar = [] ;
47609         
47610         try {
47611             //Roo.log(this.value);
47612             if (typeof(this.value) == 'undefined') {
47613                 return [];
47614             }
47615             var ar = Roo.decode(this.value);
47616             return  ar instanceof Array ? ar : []; //?? valid?
47617             
47618         } catch(e) {
47619             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47620             return [];
47621         }
47622          
47623     },
47624     expand : function ()
47625     {
47626         
47627         Roo.form.ComboCheck.superclass.expand.call(this);
47628         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47629         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47630         
47631
47632     },
47633     
47634     collapse : function(){
47635         Roo.form.ComboCheck.superclass.collapse.call(this);
47636         var sl = this.view.getSelectedIndexes();
47637         var st = this.store;
47638         var nv = [];
47639         var tv = [];
47640         var r;
47641         Roo.each(sl, function(i) {
47642             r = st.getAt(i);
47643             nv.push(r.get(this.valueField));
47644         },this);
47645         this.setValue(Roo.encode(nv));
47646         if (this.value != this.valueBefore) {
47647
47648             this.fireEvent('change', this, this.value, this.valueBefore);
47649             this.valueBefore = this.value;
47650         }
47651         
47652     },
47653     
47654     setValue : function(v){
47655         // Roo.log(v);
47656         this.value = v;
47657         
47658         var vals = this.getValueArray();
47659         var tv = [];
47660         Roo.each(vals, function(k) {
47661             var r = this.findRecord(this.valueField, k);
47662             if(r){
47663                 tv.push(r.data[this.displayField]);
47664             }else if(this.valueNotFoundText !== undefined){
47665                 tv.push( this.valueNotFoundText );
47666             }
47667         },this);
47668        // Roo.log(tv);
47669         
47670         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47671         this.hiddenField.value = v;
47672         this.value = v;
47673     }
47674     
47675 });/*
47676  * Based on:
47677  * Ext JS Library 1.1.1
47678  * Copyright(c) 2006-2007, Ext JS, LLC.
47679  *
47680  * Originally Released Under LGPL - original licence link has changed is not relivant.
47681  *
47682  * Fork - LGPL
47683  * <script type="text/javascript">
47684  */
47685  
47686 /**
47687  * @class Roo.form.Signature
47688  * @extends Roo.form.Field
47689  * Signature field.  
47690  * @constructor
47691  * 
47692  * @param {Object} config Configuration options
47693  */
47694
47695 Roo.form.Signature = function(config){
47696     Roo.form.Signature.superclass.constructor.call(this, config);
47697     
47698     this.addEvents({// not in used??
47699          /**
47700          * @event confirm
47701          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47702              * @param {Roo.form.Signature} combo This combo box
47703              */
47704         'confirm' : true,
47705         /**
47706          * @event reset
47707          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47708              * @param {Roo.form.ComboBox} combo This combo box
47709              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47710              */
47711         'reset' : true
47712     });
47713 };
47714
47715 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47716     /**
47717      * @cfg {Object} labels Label to use when rendering a form.
47718      * defaults to 
47719      * labels : { 
47720      *      clear : "Clear",
47721      *      confirm : "Confirm"
47722      *  }
47723      */
47724     labels : { 
47725         clear : "Clear",
47726         confirm : "Confirm"
47727     },
47728     /**
47729      * @cfg {Number} width The signature panel width (defaults to 300)
47730      */
47731     width: 300,
47732     /**
47733      * @cfg {Number} height The signature panel height (defaults to 100)
47734      */
47735     height : 100,
47736     /**
47737      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47738      */
47739     allowBlank : false,
47740     
47741     //private
47742     // {Object} signPanel The signature SVG panel element (defaults to {})
47743     signPanel : {},
47744     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47745     isMouseDown : false,
47746     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47747     isConfirmed : false,
47748     // {String} signatureTmp SVG mapping string (defaults to empty string)
47749     signatureTmp : '',
47750     
47751     
47752     defaultAutoCreate : { // modified by initCompnoent..
47753         tag: "input",
47754         type:"hidden"
47755     },
47756
47757     // private
47758     onRender : function(ct, position){
47759         
47760         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47761         
47762         this.wrap = this.el.wrap({
47763             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47764         });
47765         
47766         this.createToolbar(this);
47767         this.signPanel = this.wrap.createChild({
47768                 tag: 'div',
47769                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47770             }, this.el
47771         );
47772             
47773         this.svgID = Roo.id();
47774         this.svgEl = this.signPanel.createChild({
47775               xmlns : 'http://www.w3.org/2000/svg',
47776               tag : 'svg',
47777               id : this.svgID + "-svg",
47778               width: this.width,
47779               height: this.height,
47780               viewBox: '0 0 '+this.width+' '+this.height,
47781               cn : [
47782                 {
47783                     tag: "rect",
47784                     id: this.svgID + "-svg-r",
47785                     width: this.width,
47786                     height: this.height,
47787                     fill: "#ffa"
47788                 },
47789                 {
47790                     tag: "line",
47791                     id: this.svgID + "-svg-l",
47792                     x1: "0", // start
47793                     y1: (this.height*0.8), // start set the line in 80% of height
47794                     x2: this.width, // end
47795                     y2: (this.height*0.8), // end set the line in 80% of height
47796                     'stroke': "#666",
47797                     'stroke-width': "1",
47798                     'stroke-dasharray': "3",
47799                     'shape-rendering': "crispEdges",
47800                     'pointer-events': "none"
47801                 },
47802                 {
47803                     tag: "path",
47804                     id: this.svgID + "-svg-p",
47805                     'stroke': "navy",
47806                     'stroke-width': "3",
47807                     'fill': "none",
47808                     'pointer-events': 'none'
47809                 }
47810               ]
47811         });
47812         this.createSVG();
47813         this.svgBox = this.svgEl.dom.getScreenCTM();
47814     },
47815     createSVG : function(){ 
47816         var svg = this.signPanel;
47817         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47818         var t = this;
47819
47820         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47821         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47822         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47823         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47824         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47825         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47826         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47827         
47828     },
47829     isTouchEvent : function(e){
47830         return e.type.match(/^touch/);
47831     },
47832     getCoords : function (e) {
47833         var pt    = this.svgEl.dom.createSVGPoint();
47834         pt.x = e.clientX; 
47835         pt.y = e.clientY;
47836         if (this.isTouchEvent(e)) {
47837             pt.x =  e.targetTouches[0].clientX 
47838             pt.y = e.targetTouches[0].clientY;
47839         }
47840         var a = this.svgEl.dom.getScreenCTM();
47841         var b = a.inverse();
47842         var mx = pt.matrixTransform(b);
47843         return mx.x + ',' + mx.y;
47844     },
47845     //mouse event headler 
47846     down : function (e) {
47847         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47848         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47849         
47850         this.isMouseDown = true;
47851         
47852         e.preventDefault();
47853     },
47854     move : function (e) {
47855         if (this.isMouseDown) {
47856             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47857             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47858         }
47859         
47860         e.preventDefault();
47861     },
47862     up : function (e) {
47863         this.isMouseDown = false;
47864         var sp = this.signatureTmp.split(' ');
47865         
47866         if(sp.length > 1){
47867             if(!sp[sp.length-2].match(/^L/)){
47868                 sp.pop();
47869                 sp.pop();
47870                 sp.push("");
47871                 this.signatureTmp = sp.join(" ");
47872             }
47873         }
47874         if(this.getValue() != this.signatureTmp){
47875             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47876             this.isConfirmed = false;
47877         }
47878         e.preventDefault();
47879     },
47880     
47881     /**
47882      * Protected method that will not generally be called directly. It
47883      * is called when the editor creates its toolbar. Override this method if you need to
47884      * add custom toolbar buttons.
47885      * @param {HtmlEditor} editor
47886      */
47887     createToolbar : function(editor){
47888          function btn(id, toggle, handler){
47889             var xid = fid + '-'+ id ;
47890             return {
47891                 id : xid,
47892                 cmd : id,
47893                 cls : 'x-btn-icon x-edit-'+id,
47894                 enableToggle:toggle !== false,
47895                 scope: editor, // was editor...
47896                 handler:handler||editor.relayBtnCmd,
47897                 clickEvent:'mousedown',
47898                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47899                 tabIndex:-1
47900             };
47901         }
47902         
47903         
47904         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47905         this.tb = tb;
47906         this.tb.add(
47907            {
47908                 cls : ' x-signature-btn x-signature-'+id,
47909                 scope: editor, // was editor...
47910                 handler: this.reset,
47911                 clickEvent:'mousedown',
47912                 text: this.labels.clear
47913             },
47914             {
47915                  xtype : 'Fill',
47916                  xns: Roo.Toolbar
47917             }, 
47918             {
47919                 cls : '  x-signature-btn x-signature-'+id,
47920                 scope: editor, // was editor...
47921                 handler: this.confirmHandler,
47922                 clickEvent:'mousedown',
47923                 text: this.labels.confirm
47924             }
47925         );
47926     
47927     },
47928     //public
47929     /**
47930      * when user is clicked confirm then show this image.....
47931      * 
47932      * @return {String} Image Data URI
47933      */
47934     getImageDataURI : function(){
47935         var svg = this.svgEl.dom.parentNode.innerHTML;
47936         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47937         return src; 
47938     },
47939     /**
47940      * 
47941      * @return {Boolean} this.isConfirmed
47942      */
47943     getConfirmed : function(){
47944         return this.isConfirmed;
47945     },
47946     /**
47947      * 
47948      * @return {Number} this.width
47949      */
47950     getWidth : function(){
47951         return this.width;
47952     },
47953     /**
47954      * 
47955      * @return {Number} this.height
47956      */
47957     getHeight : function(){
47958         return this.height;
47959     },
47960     // private
47961     getSignature : function(){
47962         return this.signatureTmp;
47963     },
47964     // private
47965     reset : function(){
47966         this.signatureTmp = '';
47967         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47968         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47969         this.isConfirmed = false;
47970         Roo.form.Signature.superclass.reset.call(this);
47971     },
47972     setSignature : function(s){
47973         this.signatureTmp = s;
47974         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47975         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47976         this.setValue(s);
47977         this.isConfirmed = false;
47978         Roo.form.Signature.superclass.reset.call(this);
47979     }, 
47980     test : function(){
47981 //        Roo.log(this.signPanel.dom.contentWindow.up())
47982     },
47983     //private
47984     setConfirmed : function(){
47985         
47986         
47987         
47988 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47989     },
47990     // private
47991     confirmHandler : function(){
47992         if(!this.getSignature()){
47993             return;
47994         }
47995         
47996         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47997         this.setValue(this.getSignature());
47998         this.isConfirmed = true;
47999         
48000         this.fireEvent('confirm', this);
48001     },
48002     // private
48003     // Subclasses should provide the validation implementation by overriding this
48004     validateValue : function(value){
48005         if(this.allowBlank){
48006             return true;
48007         }
48008         
48009         if(this.isConfirmed){
48010             return true;
48011         }
48012         return false;
48013     }
48014 });/*
48015  * Based on:
48016  * Ext JS Library 1.1.1
48017  * Copyright(c) 2006-2007, Ext JS, LLC.
48018  *
48019  * Originally Released Under LGPL - original licence link has changed is not relivant.
48020  *
48021  * Fork - LGPL
48022  * <script type="text/javascript">
48023  */
48024  
48025
48026 /**
48027  * @class Roo.form.ComboBox
48028  * @extends Roo.form.TriggerField
48029  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48030  * @constructor
48031  * Create a new ComboBox.
48032  * @param {Object} config Configuration options
48033  */
48034 Roo.form.Select = function(config){
48035     Roo.form.Select.superclass.constructor.call(this, config);
48036      
48037 };
48038
48039 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48040     /**
48041      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48042      */
48043     /**
48044      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48045      * rendering into an Roo.Editor, defaults to false)
48046      */
48047     /**
48048      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48049      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48050      */
48051     /**
48052      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48053      */
48054     /**
48055      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48056      * the dropdown list (defaults to undefined, with no header element)
48057      */
48058
48059      /**
48060      * @cfg {String/Roo.Template} tpl The template to use to render the output
48061      */
48062      
48063     // private
48064     defaultAutoCreate : {tag: "select"  },
48065     /**
48066      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48067      */
48068     listWidth: undefined,
48069     /**
48070      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48071      * mode = 'remote' or 'text' if mode = 'local')
48072      */
48073     displayField: undefined,
48074     /**
48075      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48076      * mode = 'remote' or 'value' if mode = 'local'). 
48077      * Note: use of a valueField requires the user make a selection
48078      * in order for a value to be mapped.
48079      */
48080     valueField: undefined,
48081     
48082     
48083     /**
48084      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48085      * field's data value (defaults to the underlying DOM element's name)
48086      */
48087     hiddenName: undefined,
48088     /**
48089      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48090      */
48091     listClass: '',
48092     /**
48093      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48094      */
48095     selectedClass: 'x-combo-selected',
48096     /**
48097      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48098      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48099      * which displays a downward arrow icon).
48100      */
48101     triggerClass : 'x-form-arrow-trigger',
48102     /**
48103      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48104      */
48105     shadow:'sides',
48106     /**
48107      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48108      * anchor positions (defaults to 'tl-bl')
48109      */
48110     listAlign: 'tl-bl?',
48111     /**
48112      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48113      */
48114     maxHeight: 300,
48115     /**
48116      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48117      * query specified by the allQuery config option (defaults to 'query')
48118      */
48119     triggerAction: 'query',
48120     /**
48121      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48122      * (defaults to 4, does not apply if editable = false)
48123      */
48124     minChars : 4,
48125     /**
48126      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48127      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48128      */
48129     typeAhead: false,
48130     /**
48131      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48132      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48133      */
48134     queryDelay: 500,
48135     /**
48136      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48137      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48138      */
48139     pageSize: 0,
48140     /**
48141      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48142      * when editable = true (defaults to false)
48143      */
48144     selectOnFocus:false,
48145     /**
48146      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48147      */
48148     queryParam: 'query',
48149     /**
48150      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48151      * when mode = 'remote' (defaults to 'Loading...')
48152      */
48153     loadingText: 'Loading...',
48154     /**
48155      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48156      */
48157     resizable: false,
48158     /**
48159      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48160      */
48161     handleHeight : 8,
48162     /**
48163      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48164      * traditional select (defaults to true)
48165      */
48166     editable: true,
48167     /**
48168      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48169      */
48170     allQuery: '',
48171     /**
48172      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48173      */
48174     mode: 'remote',
48175     /**
48176      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48177      * listWidth has a higher value)
48178      */
48179     minListWidth : 70,
48180     /**
48181      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48182      * allow the user to set arbitrary text into the field (defaults to false)
48183      */
48184     forceSelection:false,
48185     /**
48186      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48187      * if typeAhead = true (defaults to 250)
48188      */
48189     typeAheadDelay : 250,
48190     /**
48191      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48192      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48193      */
48194     valueNotFoundText : undefined,
48195     
48196     /**
48197      * @cfg {String} defaultValue The value displayed after loading the store.
48198      */
48199     defaultValue: '',
48200     
48201     /**
48202      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48203      */
48204     blockFocus : false,
48205     
48206     /**
48207      * @cfg {Boolean} disableClear Disable showing of clear button.
48208      */
48209     disableClear : false,
48210     /**
48211      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48212      */
48213     alwaysQuery : false,
48214     
48215     //private
48216     addicon : false,
48217     editicon: false,
48218     
48219     // element that contains real text value.. (when hidden is used..)
48220      
48221     // private
48222     onRender : function(ct, position){
48223         Roo.form.Field.prototype.onRender.call(this, ct, position);
48224         
48225         if(this.store){
48226             this.store.on('beforeload', this.onBeforeLoad, this);
48227             this.store.on('load', this.onLoad, this);
48228             this.store.on('loadexception', this.onLoadException, this);
48229             this.store.load({});
48230         }
48231         
48232         
48233         
48234     },
48235
48236     // private
48237     initEvents : function(){
48238         //Roo.form.ComboBox.superclass.initEvents.call(this);
48239  
48240     },
48241
48242     onDestroy : function(){
48243        
48244         if(this.store){
48245             this.store.un('beforeload', this.onBeforeLoad, this);
48246             this.store.un('load', this.onLoad, this);
48247             this.store.un('loadexception', this.onLoadException, this);
48248         }
48249         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48250     },
48251
48252     // private
48253     fireKey : function(e){
48254         if(e.isNavKeyPress() && !this.list.isVisible()){
48255             this.fireEvent("specialkey", this, e);
48256         }
48257     },
48258
48259     // private
48260     onResize: function(w, h){
48261         
48262         return; 
48263     
48264         
48265     },
48266
48267     /**
48268      * Allow or prevent the user from directly editing the field text.  If false is passed,
48269      * the user will only be able to select from the items defined in the dropdown list.  This method
48270      * is the runtime equivalent of setting the 'editable' config option at config time.
48271      * @param {Boolean} value True to allow the user to directly edit the field text
48272      */
48273     setEditable : function(value){
48274          
48275     },
48276
48277     // private
48278     onBeforeLoad : function(){
48279         
48280         Roo.log("Select before load");
48281         return;
48282     
48283         this.innerList.update(this.loadingText ?
48284                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48285         //this.restrictHeight();
48286         this.selectedIndex = -1;
48287     },
48288
48289     // private
48290     onLoad : function(){
48291
48292     
48293         var dom = this.el.dom;
48294         dom.innerHTML = '';
48295          var od = dom.ownerDocument;
48296          
48297         if (this.emptyText) {
48298             var op = od.createElement('option');
48299             op.setAttribute('value', '');
48300             op.innerHTML = String.format('{0}', this.emptyText);
48301             dom.appendChild(op);
48302         }
48303         if(this.store.getCount() > 0){
48304            
48305             var vf = this.valueField;
48306             var df = this.displayField;
48307             this.store.data.each(function(r) {
48308                 // which colmsn to use... testing - cdoe / title..
48309                 var op = od.createElement('option');
48310                 op.setAttribute('value', r.data[vf]);
48311                 op.innerHTML = String.format('{0}', r.data[df]);
48312                 dom.appendChild(op);
48313             });
48314             if (typeof(this.defaultValue != 'undefined')) {
48315                 this.setValue(this.defaultValue);
48316             }
48317             
48318              
48319         }else{
48320             //this.onEmptyResults();
48321         }
48322         //this.el.focus();
48323     },
48324     // private
48325     onLoadException : function()
48326     {
48327         dom.innerHTML = '';
48328             
48329         Roo.log("Select on load exception");
48330         return;
48331     
48332         this.collapse();
48333         Roo.log(this.store.reader.jsonData);
48334         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48335             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48336         }
48337         
48338         
48339     },
48340     // private
48341     onTypeAhead : function(){
48342          
48343     },
48344
48345     // private
48346     onSelect : function(record, index){
48347         Roo.log('on select?');
48348         return;
48349         if(this.fireEvent('beforeselect', this, record, index) !== false){
48350             this.setFromData(index > -1 ? record.data : false);
48351             this.collapse();
48352             this.fireEvent('select', this, record, index);
48353         }
48354     },
48355
48356     /**
48357      * Returns the currently selected field value or empty string if no value is set.
48358      * @return {String} value The selected value
48359      */
48360     getValue : function(){
48361         var dom = this.el.dom;
48362         this.value = dom.options[dom.selectedIndex].value;
48363         return this.value;
48364         
48365     },
48366
48367     /**
48368      * Clears any text/value currently set in the field
48369      */
48370     clearValue : function(){
48371         this.value = '';
48372         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48373         
48374     },
48375
48376     /**
48377      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48378      * will be displayed in the field.  If the value does not match the data value of an existing item,
48379      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48380      * Otherwise the field will be blank (although the value will still be set).
48381      * @param {String} value The value to match
48382      */
48383     setValue : function(v){
48384         var d = this.el.dom;
48385         for (var i =0; i < d.options.length;i++) {
48386             if (v == d.options[i].value) {
48387                 d.selectedIndex = i;
48388                 this.value = v;
48389                 return;
48390             }
48391         }
48392         this.clearValue();
48393     },
48394     /**
48395      * @property {Object} the last set data for the element
48396      */
48397     
48398     lastData : false,
48399     /**
48400      * Sets the value of the field based on a object which is related to the record format for the store.
48401      * @param {Object} value the value to set as. or false on reset?
48402      */
48403     setFromData : function(o){
48404         Roo.log('setfrom data?');
48405          
48406         
48407         
48408     },
48409     // private
48410     reset : function(){
48411         this.clearValue();
48412     },
48413     // private
48414     findRecord : function(prop, value){
48415         
48416         return false;
48417     
48418         var record;
48419         if(this.store.getCount() > 0){
48420             this.store.each(function(r){
48421                 if(r.data[prop] == value){
48422                     record = r;
48423                     return false;
48424                 }
48425                 return true;
48426             });
48427         }
48428         return record;
48429     },
48430     
48431     getName: function()
48432     {
48433         // returns hidden if it's set..
48434         if (!this.rendered) {return ''};
48435         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48436         
48437     },
48438      
48439
48440     
48441
48442     // private
48443     onEmptyResults : function(){
48444         Roo.log('empty results');
48445         //this.collapse();
48446     },
48447
48448     /**
48449      * Returns true if the dropdown list is expanded, else false.
48450      */
48451     isExpanded : function(){
48452         return false;
48453     },
48454
48455     /**
48456      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48458      * @param {String} value The data value of the item to select
48459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48460      * selected item if it is not currently in view (defaults to true)
48461      * @return {Boolean} True if the value matched an item in the list, else false
48462      */
48463     selectByValue : function(v, scrollIntoView){
48464         Roo.log('select By Value');
48465         return false;
48466     
48467         if(v !== undefined && v !== null){
48468             var r = this.findRecord(this.valueField || this.displayField, v);
48469             if(r){
48470                 this.select(this.store.indexOf(r), scrollIntoView);
48471                 return true;
48472             }
48473         }
48474         return false;
48475     },
48476
48477     /**
48478      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48479      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48480      * @param {Number} index The zero-based index of the list item to select
48481      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48482      * selected item if it is not currently in view (defaults to true)
48483      */
48484     select : function(index, scrollIntoView){
48485         Roo.log('select ');
48486         return  ;
48487         
48488         this.selectedIndex = index;
48489         this.view.select(index);
48490         if(scrollIntoView !== false){
48491             var el = this.view.getNode(index);
48492             if(el){
48493                 this.innerList.scrollChildIntoView(el, false);
48494             }
48495         }
48496     },
48497
48498       
48499
48500     // private
48501     validateBlur : function(){
48502         
48503         return;
48504         
48505     },
48506
48507     // private
48508     initQuery : function(){
48509         this.doQuery(this.getRawValue());
48510     },
48511
48512     // private
48513     doForce : function(){
48514         if(this.el.dom.value.length > 0){
48515             this.el.dom.value =
48516                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48517              
48518         }
48519     },
48520
48521     /**
48522      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48523      * query allowing the query action to be canceled if needed.
48524      * @param {String} query The SQL query to execute
48525      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48526      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48527      * saved in the current store (defaults to false)
48528      */
48529     doQuery : function(q, forceAll){
48530         
48531         Roo.log('doQuery?');
48532         if(q === undefined || q === null){
48533             q = '';
48534         }
48535         var qe = {
48536             query: q,
48537             forceAll: forceAll,
48538             combo: this,
48539             cancel:false
48540         };
48541         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48542             return false;
48543         }
48544         q = qe.query;
48545         forceAll = qe.forceAll;
48546         if(forceAll === true || (q.length >= this.minChars)){
48547             if(this.lastQuery != q || this.alwaysQuery){
48548                 this.lastQuery = q;
48549                 if(this.mode == 'local'){
48550                     this.selectedIndex = -1;
48551                     if(forceAll){
48552                         this.store.clearFilter();
48553                     }else{
48554                         this.store.filter(this.displayField, q);
48555                     }
48556                     this.onLoad();
48557                 }else{
48558                     this.store.baseParams[this.queryParam] = q;
48559                     this.store.load({
48560                         params: this.getParams(q)
48561                     });
48562                     this.expand();
48563                 }
48564             }else{
48565                 this.selectedIndex = -1;
48566                 this.onLoad();   
48567             }
48568         }
48569     },
48570
48571     // private
48572     getParams : function(q){
48573         var p = {};
48574         //p[this.queryParam] = q;
48575         if(this.pageSize){
48576             p.start = 0;
48577             p.limit = this.pageSize;
48578         }
48579         return p;
48580     },
48581
48582     /**
48583      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48584      */
48585     collapse : function(){
48586         
48587     },
48588
48589     // private
48590     collapseIf : function(e){
48591         
48592     },
48593
48594     /**
48595      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48596      */
48597     expand : function(){
48598         
48599     } ,
48600
48601     // private
48602      
48603
48604     /** 
48605     * @cfg {Boolean} grow 
48606     * @hide 
48607     */
48608     /** 
48609     * @cfg {Number} growMin 
48610     * @hide 
48611     */
48612     /** 
48613     * @cfg {Number} growMax 
48614     * @hide 
48615     */
48616     /**
48617      * @hide
48618      * @method autoSize
48619      */
48620     
48621     setWidth : function()
48622     {
48623         
48624     },
48625     getResizeEl : function(){
48626         return this.el;
48627     }
48628 });//<script type="text/javasscript">
48629  
48630
48631 /**
48632  * @class Roo.DDView
48633  * A DnD enabled version of Roo.View.
48634  * @param {Element/String} container The Element in which to create the View.
48635  * @param {String} tpl The template string used to create the markup for each element of the View
48636  * @param {Object} config The configuration properties. These include all the config options of
48637  * {@link Roo.View} plus some specific to this class.<br>
48638  * <p>
48639  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48640  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48641  * <p>
48642  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48643 .x-view-drag-insert-above {
48644         border-top:1px dotted #3366cc;
48645 }
48646 .x-view-drag-insert-below {
48647         border-bottom:1px dotted #3366cc;
48648 }
48649 </code></pre>
48650  * 
48651  */
48652  
48653 Roo.DDView = function(container, tpl, config) {
48654     Roo.DDView.superclass.constructor.apply(this, arguments);
48655     this.getEl().setStyle("outline", "0px none");
48656     this.getEl().unselectable();
48657     if (this.dragGroup) {
48658                 this.setDraggable(this.dragGroup.split(","));
48659     }
48660     if (this.dropGroup) {
48661                 this.setDroppable(this.dropGroup.split(","));
48662     }
48663     if (this.deletable) {
48664         this.setDeletable();
48665     }
48666     this.isDirtyFlag = false;
48667         this.addEvents({
48668                 "drop" : true
48669         });
48670 };
48671
48672 Roo.extend(Roo.DDView, Roo.View, {
48673 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48674 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48675 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48676 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48677
48678         isFormField: true,
48679
48680         reset: Roo.emptyFn,
48681         
48682         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48683
48684         validate: function() {
48685                 return true;
48686         },
48687         
48688         destroy: function() {
48689                 this.purgeListeners();
48690                 this.getEl.removeAllListeners();
48691                 this.getEl().remove();
48692                 if (this.dragZone) {
48693                         if (this.dragZone.destroy) {
48694                                 this.dragZone.destroy();
48695                         }
48696                 }
48697                 if (this.dropZone) {
48698                         if (this.dropZone.destroy) {
48699                                 this.dropZone.destroy();
48700                         }
48701                 }
48702         },
48703
48704 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48705         getName: function() {
48706                 return this.name;
48707         },
48708
48709 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48710         setValue: function(v) {
48711                 if (!this.store) {
48712                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48713                 }
48714                 var data = {};
48715                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48716                 this.store.proxy = new Roo.data.MemoryProxy(data);
48717                 this.store.load();
48718         },
48719
48720 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48721         getValue: function() {
48722                 var result = '(';
48723                 this.store.each(function(rec) {
48724                         result += rec.id + ',';
48725                 });
48726                 return result.substr(0, result.length - 1) + ')';
48727         },
48728         
48729         getIds: function() {
48730                 var i = 0, result = new Array(this.store.getCount());
48731                 this.store.each(function(rec) {
48732                         result[i++] = rec.id;
48733                 });
48734                 return result;
48735         },
48736         
48737         isDirty: function() {
48738                 return this.isDirtyFlag;
48739         },
48740
48741 /**
48742  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48743  *      whole Element becomes the target, and this causes the drop gesture to append.
48744  */
48745     getTargetFromEvent : function(e) {
48746                 var target = e.getTarget();
48747                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48748                 target = target.parentNode;
48749                 }
48750                 if (!target) {
48751                         target = this.el.dom.lastChild || this.el.dom;
48752                 }
48753                 return target;
48754     },
48755
48756 /**
48757  *      Create the drag data which consists of an object which has the property "ddel" as
48758  *      the drag proxy element. 
48759  */
48760     getDragData : function(e) {
48761         var target = this.findItemFromChild(e.getTarget());
48762                 if(target) {
48763                         this.handleSelection(e);
48764                         var selNodes = this.getSelectedNodes();
48765             var dragData = {
48766                 source: this,
48767                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48768                 nodes: selNodes,
48769                 records: []
48770                         };
48771                         var selectedIndices = this.getSelectedIndexes();
48772                         for (var i = 0; i < selectedIndices.length; i++) {
48773                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48774                         }
48775                         if (selNodes.length == 1) {
48776                                 dragData.ddel = target.cloneNode(true); // the div element
48777                         } else {
48778                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48779                                 div.className = 'multi-proxy';
48780                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48781                                         div.appendChild(selNodes[i].cloneNode(true));
48782                                 }
48783                                 dragData.ddel = div;
48784                         }
48785             //console.log(dragData)
48786             //console.log(dragData.ddel.innerHTML)
48787                         return dragData;
48788                 }
48789         //console.log('nodragData')
48790                 return false;
48791     },
48792     
48793 /**     Specify to which ddGroup items in this DDView may be dragged. */
48794     setDraggable: function(ddGroup) {
48795         if (ddGroup instanceof Array) {
48796                 Roo.each(ddGroup, this.setDraggable, this);
48797                 return;
48798         }
48799         if (this.dragZone) {
48800                 this.dragZone.addToGroup(ddGroup);
48801         } else {
48802                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48803                                 containerScroll: true,
48804                                 ddGroup: ddGroup 
48805
48806                         });
48807 //                      Draggability implies selection. DragZone's mousedown selects the element.
48808                         if (!this.multiSelect) { this.singleSelect = true; }
48809
48810 //                      Wire the DragZone's handlers up to methods in *this*
48811                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48812                 }
48813     },
48814
48815 /**     Specify from which ddGroup this DDView accepts drops. */
48816     setDroppable: function(ddGroup) {
48817         if (ddGroup instanceof Array) {
48818                 Roo.each(ddGroup, this.setDroppable, this);
48819                 return;
48820         }
48821         if (this.dropZone) {
48822                 this.dropZone.addToGroup(ddGroup);
48823         } else {
48824                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48825                                 containerScroll: true,
48826                                 ddGroup: ddGroup
48827                         });
48828
48829 //                      Wire the DropZone's handlers up to methods in *this*
48830                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48831                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48832                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48833                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48834                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48835                 }
48836     },
48837
48838 /**     Decide whether to drop above or below a View node. */
48839     getDropPoint : function(e, n, dd){
48840         if (n == this.el.dom) { return "above"; }
48841                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48842                 var c = t + (b - t) / 2;
48843                 var y = Roo.lib.Event.getPageY(e);
48844                 if(y <= c) {
48845                         return "above";
48846                 }else{
48847                         return "below";
48848                 }
48849     },
48850
48851     onNodeEnter : function(n, dd, e, data){
48852                 return false;
48853     },
48854     
48855     onNodeOver : function(n, dd, e, data){
48856                 var pt = this.getDropPoint(e, n, dd);
48857                 // set the insert point style on the target node
48858                 var dragElClass = this.dropNotAllowed;
48859                 if (pt) {
48860                         var targetElClass;
48861                         if (pt == "above"){
48862                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48863                                 targetElClass = "x-view-drag-insert-above";
48864                         } else {
48865                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48866                                 targetElClass = "x-view-drag-insert-below";
48867                         }
48868                         if (this.lastInsertClass != targetElClass){
48869                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48870                                 this.lastInsertClass = targetElClass;
48871                         }
48872                 }
48873                 return dragElClass;
48874         },
48875
48876     onNodeOut : function(n, dd, e, data){
48877                 this.removeDropIndicators(n);
48878     },
48879
48880     onNodeDrop : function(n, dd, e, data){
48881         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48882                 return false;
48883         }
48884         var pt = this.getDropPoint(e, n, dd);
48885                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48886                 if (pt == "below") { insertAt++; }
48887                 for (var i = 0; i < data.records.length; i++) {
48888                         var r = data.records[i];
48889                         var dup = this.store.getById(r.id);
48890                         if (dup && (dd != this.dragZone)) {
48891                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48892                         } else {
48893                                 if (data.copy) {
48894                                         this.store.insert(insertAt++, r.copy());
48895                                 } else {
48896                                         data.source.isDirtyFlag = true;
48897                                         r.store.remove(r);
48898                                         this.store.insert(insertAt++, r);
48899                                 }
48900                                 this.isDirtyFlag = true;
48901                         }
48902                 }
48903                 this.dragZone.cachedTarget = null;
48904                 return true;
48905     },
48906
48907     removeDropIndicators : function(n){
48908                 if(n){
48909                         Roo.fly(n).removeClass([
48910                                 "x-view-drag-insert-above",
48911                                 "x-view-drag-insert-below"]);
48912                         this.lastInsertClass = "_noclass";
48913                 }
48914     },
48915
48916 /**
48917  *      Utility method. Add a delete option to the DDView's context menu.
48918  *      @param {String} imageUrl The URL of the "delete" icon image.
48919  */
48920         setDeletable: function(imageUrl) {
48921                 if (!this.singleSelect && !this.multiSelect) {
48922                         this.singleSelect = true;
48923                 }
48924                 var c = this.getContextMenu();
48925                 this.contextMenu.on("itemclick", function(item) {
48926                         switch (item.id) {
48927                                 case "delete":
48928                                         this.remove(this.getSelectedIndexes());
48929                                         break;
48930                         }
48931                 }, this);
48932                 this.contextMenu.add({
48933                         icon: imageUrl,
48934                         id: "delete",
48935                         text: 'Delete'
48936                 });
48937         },
48938         
48939 /**     Return the context menu for this DDView. */
48940         getContextMenu: function() {
48941                 if (!this.contextMenu) {
48942 //                      Create the View's context menu
48943                         this.contextMenu = new Roo.menu.Menu({
48944                                 id: this.id + "-contextmenu"
48945                         });
48946                         this.el.on("contextmenu", this.showContextMenu, this);
48947                 }
48948                 return this.contextMenu;
48949         },
48950         
48951         disableContextMenu: function() {
48952                 if (this.contextMenu) {
48953                         this.el.un("contextmenu", this.showContextMenu, this);
48954                 }
48955         },
48956
48957         showContextMenu: function(e, item) {
48958         item = this.findItemFromChild(e.getTarget());
48959                 if (item) {
48960                         e.stopEvent();
48961                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48962                         this.contextMenu.showAt(e.getXY());
48963             }
48964     },
48965
48966 /**
48967  *      Remove {@link Roo.data.Record}s at the specified indices.
48968  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48969  */
48970     remove: function(selectedIndices) {
48971                 selectedIndices = [].concat(selectedIndices);
48972                 for (var i = 0; i < selectedIndices.length; i++) {
48973                         var rec = this.store.getAt(selectedIndices[i]);
48974                         this.store.remove(rec);
48975                 }
48976     },
48977
48978 /**
48979  *      Double click fires the event, but also, if this is draggable, and there is only one other
48980  *      related DropZone, it transfers the selected node.
48981  */
48982     onDblClick : function(e){
48983         var item = this.findItemFromChild(e.getTarget());
48984         if(item){
48985             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48986                 return false;
48987             }
48988             if (this.dragGroup) {
48989                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48990                     while (targets.indexOf(this.dropZone) > -1) {
48991                             targets.remove(this.dropZone);
48992                                 }
48993                     if (targets.length == 1) {
48994                                         this.dragZone.cachedTarget = null;
48995                         var el = Roo.get(targets[0].getEl());
48996                         var box = el.getBox(true);
48997                         targets[0].onNodeDrop(el.dom, {
48998                                 target: el.dom,
48999                                 xy: [box.x, box.y + box.height - 1]
49000                         }, null, this.getDragData(e));
49001                     }
49002                 }
49003         }
49004     },
49005     
49006     handleSelection: function(e) {
49007                 this.dragZone.cachedTarget = null;
49008         var item = this.findItemFromChild(e.getTarget());
49009         if (!item) {
49010                 this.clearSelections(true);
49011                 return;
49012         }
49013                 if (item && (this.multiSelect || this.singleSelect)){
49014                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49015                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49016                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49017                                 this.unselect(item);
49018                         } else {
49019                                 this.select(item, this.multiSelect && e.ctrlKey);
49020                                 this.lastSelection = item;
49021                         }
49022                 }
49023     },
49024
49025     onItemClick : function(item, index, e){
49026                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49027                         return false;
49028                 }
49029                 return true;
49030     },
49031
49032     unselect : function(nodeInfo, suppressEvent){
49033                 var node = this.getNode(nodeInfo);
49034                 if(node && this.isSelected(node)){
49035                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49036                                 Roo.fly(node).removeClass(this.selectedClass);
49037                                 this.selections.remove(node);
49038                                 if(!suppressEvent){
49039                                         this.fireEvent("selectionchange", this, this.selections);
49040                                 }
49041                         }
49042                 }
49043     }
49044 });
49045 /*
49046  * Based on:
49047  * Ext JS Library 1.1.1
49048  * Copyright(c) 2006-2007, Ext JS, LLC.
49049  *
49050  * Originally Released Under LGPL - original licence link has changed is not relivant.
49051  *
49052  * Fork - LGPL
49053  * <script type="text/javascript">
49054  */
49055  
49056 /**
49057  * @class Roo.LayoutManager
49058  * @extends Roo.util.Observable
49059  * Base class for layout managers.
49060  */
49061 Roo.LayoutManager = function(container, config){
49062     Roo.LayoutManager.superclass.constructor.call(this);
49063     this.el = Roo.get(container);
49064     // ie scrollbar fix
49065     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49066         document.body.scroll = "no";
49067     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49068         this.el.position('relative');
49069     }
49070     this.id = this.el.id;
49071     this.el.addClass("x-layout-container");
49072     /** false to disable window resize monitoring @type Boolean */
49073     this.monitorWindowResize = true;
49074     this.regions = {};
49075     this.addEvents({
49076         /**
49077          * @event layout
49078          * Fires when a layout is performed. 
49079          * @param {Roo.LayoutManager} this
49080          */
49081         "layout" : true,
49082         /**
49083          * @event regionresized
49084          * Fires when the user resizes a region. 
49085          * @param {Roo.LayoutRegion} region The resized region
49086          * @param {Number} newSize The new size (width for east/west, height for north/south)
49087          */
49088         "regionresized" : true,
49089         /**
49090          * @event regioncollapsed
49091          * Fires when a region is collapsed. 
49092          * @param {Roo.LayoutRegion} region The collapsed region
49093          */
49094         "regioncollapsed" : true,
49095         /**
49096          * @event regionexpanded
49097          * Fires when a region is expanded.  
49098          * @param {Roo.LayoutRegion} region The expanded region
49099          */
49100         "regionexpanded" : true
49101     });
49102     this.updating = false;
49103     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49104 };
49105
49106 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49107     /**
49108      * Returns true if this layout is currently being updated
49109      * @return {Boolean}
49110      */
49111     isUpdating : function(){
49112         return this.updating; 
49113     },
49114     
49115     /**
49116      * Suspend the LayoutManager from doing auto-layouts while
49117      * making multiple add or remove calls
49118      */
49119     beginUpdate : function(){
49120         this.updating = true;    
49121     },
49122     
49123     /**
49124      * Restore auto-layouts and optionally disable the manager from performing a layout
49125      * @param {Boolean} noLayout true to disable a layout update 
49126      */
49127     endUpdate : function(noLayout){
49128         this.updating = false;
49129         if(!noLayout){
49130             this.layout();
49131         }    
49132     },
49133     
49134     layout: function(){
49135         
49136     },
49137     
49138     onRegionResized : function(region, newSize){
49139         this.fireEvent("regionresized", region, newSize);
49140         this.layout();
49141     },
49142     
49143     onRegionCollapsed : function(region){
49144         this.fireEvent("regioncollapsed", region);
49145     },
49146     
49147     onRegionExpanded : function(region){
49148         this.fireEvent("regionexpanded", region);
49149     },
49150         
49151     /**
49152      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49153      * performs box-model adjustments.
49154      * @return {Object} The size as an object {width: (the width), height: (the height)}
49155      */
49156     getViewSize : function(){
49157         var size;
49158         if(this.el.dom != document.body){
49159             size = this.el.getSize();
49160         }else{
49161             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49162         }
49163         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49164         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49165         return size;
49166     },
49167     
49168     /**
49169      * Returns the Element this layout is bound to.
49170      * @return {Roo.Element}
49171      */
49172     getEl : function(){
49173         return this.el;
49174     },
49175     
49176     /**
49177      * Returns the specified region.
49178      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49179      * @return {Roo.LayoutRegion}
49180      */
49181     getRegion : function(target){
49182         return this.regions[target.toLowerCase()];
49183     },
49184     
49185     onWindowResize : function(){
49186         if(this.monitorWindowResize){
49187             this.layout();
49188         }
49189     }
49190 });/*
49191  * Based on:
49192  * Ext JS Library 1.1.1
49193  * Copyright(c) 2006-2007, Ext JS, LLC.
49194  *
49195  * Originally Released Under LGPL - original licence link has changed is not relivant.
49196  *
49197  * Fork - LGPL
49198  * <script type="text/javascript">
49199  */
49200 /**
49201  * @class Roo.BorderLayout
49202  * @extends Roo.LayoutManager
49203  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49204  * please see: <br><br>
49205  * <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>
49206  * <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>
49207  * Example:
49208  <pre><code>
49209  var layout = new Roo.BorderLayout(document.body, {
49210     north: {
49211         initialSize: 25,
49212         titlebar: false
49213     },
49214     west: {
49215         split:true,
49216         initialSize: 200,
49217         minSize: 175,
49218         maxSize: 400,
49219         titlebar: true,
49220         collapsible: true
49221     },
49222     east: {
49223         split:true,
49224         initialSize: 202,
49225         minSize: 175,
49226         maxSize: 400,
49227         titlebar: true,
49228         collapsible: true
49229     },
49230     south: {
49231         split:true,
49232         initialSize: 100,
49233         minSize: 100,
49234         maxSize: 200,
49235         titlebar: true,
49236         collapsible: true
49237     },
49238     center: {
49239         titlebar: true,
49240         autoScroll:true,
49241         resizeTabs: true,
49242         minTabWidth: 50,
49243         preferredTabWidth: 150
49244     }
49245 });
49246
49247 // shorthand
49248 var CP = Roo.ContentPanel;
49249
49250 layout.beginUpdate();
49251 layout.add("north", new CP("north", "North"));
49252 layout.add("south", new CP("south", {title: "South", closable: true}));
49253 layout.add("west", new CP("west", {title: "West"}));
49254 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49255 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49256 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49257 layout.getRegion("center").showPanel("center1");
49258 layout.endUpdate();
49259 </code></pre>
49260
49261 <b>The container the layout is rendered into can be either the body element or any other element.
49262 If it is not the body element, the container needs to either be an absolute positioned element,
49263 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49264 the container size if it is not the body element.</b>
49265
49266 * @constructor
49267 * Create a new BorderLayout
49268 * @param {String/HTMLElement/Element} container The container this layout is bound to
49269 * @param {Object} config Configuration options
49270  */
49271 Roo.BorderLayout = function(container, config){
49272     config = config || {};
49273     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49274     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49275     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49276         var target = this.factory.validRegions[i];
49277         if(config[target]){
49278             this.addRegion(target, config[target]);
49279         }
49280     }
49281 };
49282
49283 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49284     /**
49285      * Creates and adds a new region if it doesn't already exist.
49286      * @param {String} target The target region key (north, south, east, west or center).
49287      * @param {Object} config The regions config object
49288      * @return {BorderLayoutRegion} The new region
49289      */
49290     addRegion : function(target, config){
49291         if(!this.regions[target]){
49292             var r = this.factory.create(target, this, config);
49293             this.bindRegion(target, r);
49294         }
49295         return this.regions[target];
49296     },
49297
49298     // private (kinda)
49299     bindRegion : function(name, r){
49300         this.regions[name] = r;
49301         r.on("visibilitychange", this.layout, this);
49302         r.on("paneladded", this.layout, this);
49303         r.on("panelremoved", this.layout, this);
49304         r.on("invalidated", this.layout, this);
49305         r.on("resized", this.onRegionResized, this);
49306         r.on("collapsed", this.onRegionCollapsed, this);
49307         r.on("expanded", this.onRegionExpanded, this);
49308     },
49309
49310     /**
49311      * Performs a layout update.
49312      */
49313     layout : function(){
49314         if(this.updating) return;
49315         var size = this.getViewSize();
49316         var w = size.width;
49317         var h = size.height;
49318         var centerW = w;
49319         var centerH = h;
49320         var centerY = 0;
49321         var centerX = 0;
49322         //var x = 0, y = 0;
49323
49324         var rs = this.regions;
49325         var north = rs["north"];
49326         var south = rs["south"]; 
49327         var west = rs["west"];
49328         var east = rs["east"];
49329         var center = rs["center"];
49330         //if(this.hideOnLayout){ // not supported anymore
49331             //c.el.setStyle("display", "none");
49332         //}
49333         if(north && north.isVisible()){
49334             var b = north.getBox();
49335             var m = north.getMargins();
49336             b.width = w - (m.left+m.right);
49337             b.x = m.left;
49338             b.y = m.top;
49339             centerY = b.height + b.y + m.bottom;
49340             centerH -= centerY;
49341             north.updateBox(this.safeBox(b));
49342         }
49343         if(south && south.isVisible()){
49344             var b = south.getBox();
49345             var m = south.getMargins();
49346             b.width = w - (m.left+m.right);
49347             b.x = m.left;
49348             var totalHeight = (b.height + m.top + m.bottom);
49349             b.y = h - totalHeight + m.top;
49350             centerH -= totalHeight;
49351             south.updateBox(this.safeBox(b));
49352         }
49353         if(west && west.isVisible()){
49354             var b = west.getBox();
49355             var m = west.getMargins();
49356             b.height = centerH - (m.top+m.bottom);
49357             b.x = m.left;
49358             b.y = centerY + m.top;
49359             var totalWidth = (b.width + m.left + m.right);
49360             centerX += totalWidth;
49361             centerW -= totalWidth;
49362             west.updateBox(this.safeBox(b));
49363         }
49364         if(east && east.isVisible()){
49365             var b = east.getBox();
49366             var m = east.getMargins();
49367             b.height = centerH - (m.top+m.bottom);
49368             var totalWidth = (b.width + m.left + m.right);
49369             b.x = w - totalWidth + m.left;
49370             b.y = centerY + m.top;
49371             centerW -= totalWidth;
49372             east.updateBox(this.safeBox(b));
49373         }
49374         if(center){
49375             var m = center.getMargins();
49376             var centerBox = {
49377                 x: centerX + m.left,
49378                 y: centerY + m.top,
49379                 width: centerW - (m.left+m.right),
49380                 height: centerH - (m.top+m.bottom)
49381             };
49382             //if(this.hideOnLayout){
49383                 //center.el.setStyle("display", "block");
49384             //}
49385             center.updateBox(this.safeBox(centerBox));
49386         }
49387         this.el.repaint();
49388         this.fireEvent("layout", this);
49389     },
49390
49391     // private
49392     safeBox : function(box){
49393         box.width = Math.max(0, box.width);
49394         box.height = Math.max(0, box.height);
49395         return box;
49396     },
49397
49398     /**
49399      * Adds a ContentPanel (or subclass) to this layout.
49400      * @param {String} target The target region key (north, south, east, west or center).
49401      * @param {Roo.ContentPanel} panel The panel to add
49402      * @return {Roo.ContentPanel} The added panel
49403      */
49404     add : function(target, panel){
49405          
49406         target = target.toLowerCase();
49407         return this.regions[target].add(panel);
49408     },
49409
49410     /**
49411      * Remove a ContentPanel (or subclass) to this layout.
49412      * @param {String} target The target region key (north, south, east, west or center).
49413      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49414      * @return {Roo.ContentPanel} The removed panel
49415      */
49416     remove : function(target, panel){
49417         target = target.toLowerCase();
49418         return this.regions[target].remove(panel);
49419     },
49420
49421     /**
49422      * Searches all regions for a panel with the specified id
49423      * @param {String} panelId
49424      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49425      */
49426     findPanel : function(panelId){
49427         var rs = this.regions;
49428         for(var target in rs){
49429             if(typeof rs[target] != "function"){
49430                 var p = rs[target].getPanel(panelId);
49431                 if(p){
49432                     return p;
49433                 }
49434             }
49435         }
49436         return null;
49437     },
49438
49439     /**
49440      * Searches all regions for a panel with the specified id and activates (shows) it.
49441      * @param {String/ContentPanel} panelId The panels id or the panel itself
49442      * @return {Roo.ContentPanel} The shown panel or null
49443      */
49444     showPanel : function(panelId) {
49445       var rs = this.regions;
49446       for(var target in rs){
49447          var r = rs[target];
49448          if(typeof r != "function"){
49449             if(r.hasPanel(panelId)){
49450                return r.showPanel(panelId);
49451             }
49452          }
49453       }
49454       return null;
49455    },
49456
49457    /**
49458      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49459      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49460      */
49461     restoreState : function(provider){
49462         if(!provider){
49463             provider = Roo.state.Manager;
49464         }
49465         var sm = new Roo.LayoutStateManager();
49466         sm.init(this, provider);
49467     },
49468
49469     /**
49470      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49471      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49472      * a valid ContentPanel config object.  Example:
49473      * <pre><code>
49474 // Create the main layout
49475 var layout = new Roo.BorderLayout('main-ct', {
49476     west: {
49477         split:true,
49478         minSize: 175,
49479         titlebar: true
49480     },
49481     center: {
49482         title:'Components'
49483     }
49484 }, 'main-ct');
49485
49486 // Create and add multiple ContentPanels at once via configs
49487 layout.batchAdd({
49488    west: {
49489        id: 'source-files',
49490        autoCreate:true,
49491        title:'Ext Source Files',
49492        autoScroll:true,
49493        fitToFrame:true
49494    },
49495    center : {
49496        el: cview,
49497        autoScroll:true,
49498        fitToFrame:true,
49499        toolbar: tb,
49500        resizeEl:'cbody'
49501    }
49502 });
49503 </code></pre>
49504      * @param {Object} regions An object containing ContentPanel configs by region name
49505      */
49506     batchAdd : function(regions){
49507         this.beginUpdate();
49508         for(var rname in regions){
49509             var lr = this.regions[rname];
49510             if(lr){
49511                 this.addTypedPanels(lr, regions[rname]);
49512             }
49513         }
49514         this.endUpdate();
49515     },
49516
49517     // private
49518     addTypedPanels : function(lr, ps){
49519         if(typeof ps == 'string'){
49520             lr.add(new Roo.ContentPanel(ps));
49521         }
49522         else if(ps instanceof Array){
49523             for(var i =0, len = ps.length; i < len; i++){
49524                 this.addTypedPanels(lr, ps[i]);
49525             }
49526         }
49527         else if(!ps.events){ // raw config?
49528             var el = ps.el;
49529             delete ps.el; // prevent conflict
49530             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49531         }
49532         else {  // panel object assumed!
49533             lr.add(ps);
49534         }
49535     },
49536     /**
49537      * Adds a xtype elements to the layout.
49538      * <pre><code>
49539
49540 layout.addxtype({
49541        xtype : 'ContentPanel',
49542        region: 'west',
49543        items: [ .... ]
49544    }
49545 );
49546
49547 layout.addxtype({
49548         xtype : 'NestedLayoutPanel',
49549         region: 'west',
49550         layout: {
49551            center: { },
49552            west: { }   
49553         },
49554         items : [ ... list of content panels or nested layout panels.. ]
49555    }
49556 );
49557 </code></pre>
49558      * @param {Object} cfg Xtype definition of item to add.
49559      */
49560     addxtype : function(cfg)
49561     {
49562         // basically accepts a pannel...
49563         // can accept a layout region..!?!?
49564         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49565         
49566         if (!cfg.xtype.match(/Panel$/)) {
49567             return false;
49568         }
49569         var ret = false;
49570         
49571         if (typeof(cfg.region) == 'undefined') {
49572             Roo.log("Failed to add Panel, region was not set");
49573             Roo.log(cfg);
49574             return false;
49575         }
49576         var region = cfg.region;
49577         delete cfg.region;
49578         
49579           
49580         var xitems = [];
49581         if (cfg.items) {
49582             xitems = cfg.items;
49583             delete cfg.items;
49584         }
49585         var nb = false;
49586         
49587         switch(cfg.xtype) 
49588         {
49589             case 'ContentPanel':  // ContentPanel (el, cfg)
49590             case 'ScrollPanel':  // ContentPanel (el, cfg)
49591             case 'ViewPanel': 
49592                 if(cfg.autoCreate) {
49593                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49594                 } else {
49595                     var el = this.el.createChild();
49596                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49597                 }
49598                 
49599                 this.add(region, ret);
49600                 break;
49601             
49602             
49603             case 'TreePanel': // our new panel!
49604                 cfg.el = this.el.createChild();
49605                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49606                 this.add(region, ret);
49607                 break;
49608             
49609             case 'NestedLayoutPanel': 
49610                 // create a new Layout (which is  a Border Layout...
49611                 var el = this.el.createChild();
49612                 var clayout = cfg.layout;
49613                 delete cfg.layout;
49614                 clayout.items   = clayout.items  || [];
49615                 // replace this exitems with the clayout ones..
49616                 xitems = clayout.items;
49617                  
49618                 
49619                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49620                     cfg.background = false;
49621                 }
49622                 var layout = new Roo.BorderLayout(el, clayout);
49623                 
49624                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49625                 //console.log('adding nested layout panel '  + cfg.toSource());
49626                 this.add(region, ret);
49627                 nb = {}; /// find first...
49628                 break;
49629                 
49630             case 'GridPanel': 
49631             
49632                 // needs grid and region
49633                 
49634                 //var el = this.getRegion(region).el.createChild();
49635                 var el = this.el.createChild();
49636                 // create the grid first...
49637                 
49638                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49639                 delete cfg.grid;
49640                 if (region == 'center' && this.active ) {
49641                     cfg.background = false;
49642                 }
49643                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49644                 
49645                 this.add(region, ret);
49646                 if (cfg.background) {
49647                     ret.on('activate', function(gp) {
49648                         if (!gp.grid.rendered) {
49649                             gp.grid.render();
49650                         }
49651                     });
49652                 } else {
49653                     grid.render();
49654                 }
49655                 break;
49656            
49657            
49658            
49659                 
49660                 
49661                 
49662             default:
49663                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49664                     
49665                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49666                     this.add(region, ret);
49667                 } else {
49668                 
49669                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49670                     return null;
49671                 }
49672                 
49673              // GridPanel (grid, cfg)
49674             
49675         }
49676         this.beginUpdate();
49677         // add children..
49678         var region = '';
49679         var abn = {};
49680         Roo.each(xitems, function(i)  {
49681             region = nb && i.region ? i.region : false;
49682             
49683             var add = ret.addxtype(i);
49684            
49685             if (region) {
49686                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49687                 if (!i.background) {
49688                     abn[region] = nb[region] ;
49689                 }
49690             }
49691             
49692         });
49693         this.endUpdate();
49694
49695         // make the last non-background panel active..
49696         //if (nb) { Roo.log(abn); }
49697         if (nb) {
49698             
49699             for(var r in abn) {
49700                 region = this.getRegion(r);
49701                 if (region) {
49702                     // tried using nb[r], but it does not work..
49703                      
49704                     region.showPanel(abn[r]);
49705                    
49706                 }
49707             }
49708         }
49709         return ret;
49710         
49711     }
49712 });
49713
49714 /**
49715  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49716  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49717  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49718  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49719  * <pre><code>
49720 // shorthand
49721 var CP = Roo.ContentPanel;
49722
49723 var layout = Roo.BorderLayout.create({
49724     north: {
49725         initialSize: 25,
49726         titlebar: false,
49727         panels: [new CP("north", "North")]
49728     },
49729     west: {
49730         split:true,
49731         initialSize: 200,
49732         minSize: 175,
49733         maxSize: 400,
49734         titlebar: true,
49735         collapsible: true,
49736         panels: [new CP("west", {title: "West"})]
49737     },
49738     east: {
49739         split:true,
49740         initialSize: 202,
49741         minSize: 175,
49742         maxSize: 400,
49743         titlebar: true,
49744         collapsible: true,
49745         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49746     },
49747     south: {
49748         split:true,
49749         initialSize: 100,
49750         minSize: 100,
49751         maxSize: 200,
49752         titlebar: true,
49753         collapsible: true,
49754         panels: [new CP("south", {title: "South", closable: true})]
49755     },
49756     center: {
49757         titlebar: true,
49758         autoScroll:true,
49759         resizeTabs: true,
49760         minTabWidth: 50,
49761         preferredTabWidth: 150,
49762         panels: [
49763             new CP("center1", {title: "Close Me", closable: true}),
49764             new CP("center2", {title: "Center Panel", closable: false})
49765         ]
49766     }
49767 }, document.body);
49768
49769 layout.getRegion("center").showPanel("center1");
49770 </code></pre>
49771  * @param config
49772  * @param targetEl
49773  */
49774 Roo.BorderLayout.create = function(config, targetEl){
49775     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49776     layout.beginUpdate();
49777     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49778     for(var j = 0, jlen = regions.length; j < jlen; j++){
49779         var lr = regions[j];
49780         if(layout.regions[lr] && config[lr].panels){
49781             var r = layout.regions[lr];
49782             var ps = config[lr].panels;
49783             layout.addTypedPanels(r, ps);
49784         }
49785     }
49786     layout.endUpdate();
49787     return layout;
49788 };
49789
49790 // private
49791 Roo.BorderLayout.RegionFactory = {
49792     // private
49793     validRegions : ["north","south","east","west","center"],
49794
49795     // private
49796     create : function(target, mgr, config){
49797         target = target.toLowerCase();
49798         if(config.lightweight || config.basic){
49799             return new Roo.BasicLayoutRegion(mgr, config, target);
49800         }
49801         switch(target){
49802             case "north":
49803                 return new Roo.NorthLayoutRegion(mgr, config);
49804             case "south":
49805                 return new Roo.SouthLayoutRegion(mgr, config);
49806             case "east":
49807                 return new Roo.EastLayoutRegion(mgr, config);
49808             case "west":
49809                 return new Roo.WestLayoutRegion(mgr, config);
49810             case "center":
49811                 return new Roo.CenterLayoutRegion(mgr, config);
49812         }
49813         throw 'Layout region "'+target+'" not supported.';
49814     }
49815 };/*
49816  * Based on:
49817  * Ext JS Library 1.1.1
49818  * Copyright(c) 2006-2007, Ext JS, LLC.
49819  *
49820  * Originally Released Under LGPL - original licence link has changed is not relivant.
49821  *
49822  * Fork - LGPL
49823  * <script type="text/javascript">
49824  */
49825  
49826 /**
49827  * @class Roo.BasicLayoutRegion
49828  * @extends Roo.util.Observable
49829  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49830  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49831  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49832  */
49833 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49834     this.mgr = mgr;
49835     this.position  = pos;
49836     this.events = {
49837         /**
49838          * @scope Roo.BasicLayoutRegion
49839          */
49840         
49841         /**
49842          * @event beforeremove
49843          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49844          * @param {Roo.LayoutRegion} this
49845          * @param {Roo.ContentPanel} panel The panel
49846          * @param {Object} e The cancel event object
49847          */
49848         "beforeremove" : true,
49849         /**
49850          * @event invalidated
49851          * Fires when the layout for this region is changed.
49852          * @param {Roo.LayoutRegion} this
49853          */
49854         "invalidated" : true,
49855         /**
49856          * @event visibilitychange
49857          * Fires when this region is shown or hidden 
49858          * @param {Roo.LayoutRegion} this
49859          * @param {Boolean} visibility true or false
49860          */
49861         "visibilitychange" : true,
49862         /**
49863          * @event paneladded
49864          * Fires when a panel is added. 
49865          * @param {Roo.LayoutRegion} this
49866          * @param {Roo.ContentPanel} panel The panel
49867          */
49868         "paneladded" : true,
49869         /**
49870          * @event panelremoved
49871          * Fires when a panel is removed. 
49872          * @param {Roo.LayoutRegion} this
49873          * @param {Roo.ContentPanel} panel The panel
49874          */
49875         "panelremoved" : true,
49876         /**
49877          * @event collapsed
49878          * Fires when this region is collapsed.
49879          * @param {Roo.LayoutRegion} this
49880          */
49881         "collapsed" : true,
49882         /**
49883          * @event expanded
49884          * Fires when this region is expanded.
49885          * @param {Roo.LayoutRegion} this
49886          */
49887         "expanded" : true,
49888         /**
49889          * @event slideshow
49890          * Fires when this region is slid into view.
49891          * @param {Roo.LayoutRegion} this
49892          */
49893         "slideshow" : true,
49894         /**
49895          * @event slidehide
49896          * Fires when this region slides out of view. 
49897          * @param {Roo.LayoutRegion} this
49898          */
49899         "slidehide" : true,
49900         /**
49901          * @event panelactivated
49902          * Fires when a panel is activated. 
49903          * @param {Roo.LayoutRegion} this
49904          * @param {Roo.ContentPanel} panel The activated panel
49905          */
49906         "panelactivated" : true,
49907         /**
49908          * @event resized
49909          * Fires when the user resizes this region. 
49910          * @param {Roo.LayoutRegion} this
49911          * @param {Number} newSize The new size (width for east/west, height for north/south)
49912          */
49913         "resized" : true
49914     };
49915     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49916     this.panels = new Roo.util.MixedCollection();
49917     this.panels.getKey = this.getPanelId.createDelegate(this);
49918     this.box = null;
49919     this.activePanel = null;
49920     // ensure listeners are added...
49921     
49922     if (config.listeners || config.events) {
49923         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49924             listeners : config.listeners || {},
49925             events : config.events || {}
49926         });
49927     }
49928     
49929     if(skipConfig !== true){
49930         this.applyConfig(config);
49931     }
49932 };
49933
49934 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49935     getPanelId : function(p){
49936         return p.getId();
49937     },
49938     
49939     applyConfig : function(config){
49940         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49941         this.config = config;
49942         
49943     },
49944     
49945     /**
49946      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49947      * the width, for horizontal (north, south) the height.
49948      * @param {Number} newSize The new width or height
49949      */
49950     resizeTo : function(newSize){
49951         var el = this.el ? this.el :
49952                  (this.activePanel ? this.activePanel.getEl() : null);
49953         if(el){
49954             switch(this.position){
49955                 case "east":
49956                 case "west":
49957                     el.setWidth(newSize);
49958                     this.fireEvent("resized", this, newSize);
49959                 break;
49960                 case "north":
49961                 case "south":
49962                     el.setHeight(newSize);
49963                     this.fireEvent("resized", this, newSize);
49964                 break;                
49965             }
49966         }
49967     },
49968     
49969     getBox : function(){
49970         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49971     },
49972     
49973     getMargins : function(){
49974         return this.margins;
49975     },
49976     
49977     updateBox : function(box){
49978         this.box = box;
49979         var el = this.activePanel.getEl();
49980         el.dom.style.left = box.x + "px";
49981         el.dom.style.top = box.y + "px";
49982         this.activePanel.setSize(box.width, box.height);
49983     },
49984     
49985     /**
49986      * Returns the container element for this region.
49987      * @return {Roo.Element}
49988      */
49989     getEl : function(){
49990         return this.activePanel;
49991     },
49992     
49993     /**
49994      * Returns true if this region is currently visible.
49995      * @return {Boolean}
49996      */
49997     isVisible : function(){
49998         return this.activePanel ? true : false;
49999     },
50000     
50001     setActivePanel : function(panel){
50002         panel = this.getPanel(panel);
50003         if(this.activePanel && this.activePanel != panel){
50004             this.activePanel.setActiveState(false);
50005             this.activePanel.getEl().setLeftTop(-10000,-10000);
50006         }
50007         this.activePanel = panel;
50008         panel.setActiveState(true);
50009         if(this.box){
50010             panel.setSize(this.box.width, this.box.height);
50011         }
50012         this.fireEvent("panelactivated", this, panel);
50013         this.fireEvent("invalidated");
50014     },
50015     
50016     /**
50017      * Show the specified panel.
50018      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50019      * @return {Roo.ContentPanel} The shown panel or null
50020      */
50021     showPanel : function(panel){
50022         if(panel = this.getPanel(panel)){
50023             this.setActivePanel(panel);
50024         }
50025         return panel;
50026     },
50027     
50028     /**
50029      * Get the active panel for this region.
50030      * @return {Roo.ContentPanel} The active panel or null
50031      */
50032     getActivePanel : function(){
50033         return this.activePanel;
50034     },
50035     
50036     /**
50037      * Add the passed ContentPanel(s)
50038      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50039      * @return {Roo.ContentPanel} The panel added (if only one was added)
50040      */
50041     add : function(panel){
50042         if(arguments.length > 1){
50043             for(var i = 0, len = arguments.length; i < len; i++) {
50044                 this.add(arguments[i]);
50045             }
50046             return null;
50047         }
50048         if(this.hasPanel(panel)){
50049             this.showPanel(panel);
50050             return panel;
50051         }
50052         var el = panel.getEl();
50053         if(el.dom.parentNode != this.mgr.el.dom){
50054             this.mgr.el.dom.appendChild(el.dom);
50055         }
50056         if(panel.setRegion){
50057             panel.setRegion(this);
50058         }
50059         this.panels.add(panel);
50060         el.setStyle("position", "absolute");
50061         if(!panel.background){
50062             this.setActivePanel(panel);
50063             if(this.config.initialSize && this.panels.getCount()==1){
50064                 this.resizeTo(this.config.initialSize);
50065             }
50066         }
50067         this.fireEvent("paneladded", this, panel);
50068         return panel;
50069     },
50070     
50071     /**
50072      * Returns true if the panel is in this region.
50073      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50074      * @return {Boolean}
50075      */
50076     hasPanel : function(panel){
50077         if(typeof panel == "object"){ // must be panel obj
50078             panel = panel.getId();
50079         }
50080         return this.getPanel(panel) ? true : false;
50081     },
50082     
50083     /**
50084      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50085      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50086      * @param {Boolean} preservePanel Overrides the config preservePanel option
50087      * @return {Roo.ContentPanel} The panel that was removed
50088      */
50089     remove : function(panel, preservePanel){
50090         panel = this.getPanel(panel);
50091         if(!panel){
50092             return null;
50093         }
50094         var e = {};
50095         this.fireEvent("beforeremove", this, panel, e);
50096         if(e.cancel === true){
50097             return null;
50098         }
50099         var panelId = panel.getId();
50100         this.panels.removeKey(panelId);
50101         return panel;
50102     },
50103     
50104     /**
50105      * Returns the panel specified or null if it's not in this region.
50106      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50107      * @return {Roo.ContentPanel}
50108      */
50109     getPanel : function(id){
50110         if(typeof id == "object"){ // must be panel obj
50111             return id;
50112         }
50113         return this.panels.get(id);
50114     },
50115     
50116     /**
50117      * Returns this regions position (north/south/east/west/center).
50118      * @return {String} 
50119      */
50120     getPosition: function(){
50121         return this.position;    
50122     }
50123 });/*
50124  * Based on:
50125  * Ext JS Library 1.1.1
50126  * Copyright(c) 2006-2007, Ext JS, LLC.
50127  *
50128  * Originally Released Under LGPL - original licence link has changed is not relivant.
50129  *
50130  * Fork - LGPL
50131  * <script type="text/javascript">
50132  */
50133  
50134 /**
50135  * @class Roo.LayoutRegion
50136  * @extends Roo.BasicLayoutRegion
50137  * This class represents a region in a layout manager.
50138  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50139  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50140  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50141  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50142  * @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})
50143  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50144  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50145  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50146  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50147  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50148  * @cfg {String}    title           The title for the region (overrides panel titles)
50149  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50150  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50151  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50152  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50153  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50154  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50155  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50156  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50157  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50158  * @cfg {Boolean}   showPin         True to show a pin button
50159  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50160  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50161  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50162  * @cfg {Number}    width           For East/West panels
50163  * @cfg {Number}    height          For North/South panels
50164  * @cfg {Boolean}   split           To show the splitter
50165  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50166  */
50167 Roo.LayoutRegion = function(mgr, config, pos){
50168     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50169     var dh = Roo.DomHelper;
50170     /** This region's container element 
50171     * @type Roo.Element */
50172     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50173     /** This region's title element 
50174     * @type Roo.Element */
50175
50176     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50177         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50178         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50179     ]}, true);
50180     this.titleEl.enableDisplayMode();
50181     /** This region's title text element 
50182     * @type HTMLElement */
50183     this.titleTextEl = this.titleEl.dom.firstChild;
50184     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50185     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50186     this.closeBtn.enableDisplayMode();
50187     this.closeBtn.on("click", this.closeClicked, this);
50188     this.closeBtn.hide();
50189
50190     this.createBody(config);
50191     this.visible = true;
50192     this.collapsed = false;
50193
50194     if(config.hideWhenEmpty){
50195         this.hide();
50196         this.on("paneladded", this.validateVisibility, this);
50197         this.on("panelremoved", this.validateVisibility, this);
50198     }
50199     this.applyConfig(config);
50200 };
50201
50202 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50203
50204     createBody : function(){
50205         /** This region's body element 
50206         * @type Roo.Element */
50207         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50208     },
50209
50210     applyConfig : function(c){
50211         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50212             var dh = Roo.DomHelper;
50213             if(c.titlebar !== false){
50214                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50215                 this.collapseBtn.on("click", this.collapse, this);
50216                 this.collapseBtn.enableDisplayMode();
50217
50218                 if(c.showPin === true || this.showPin){
50219                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50220                     this.stickBtn.enableDisplayMode();
50221                     this.stickBtn.on("click", this.expand, this);
50222                     this.stickBtn.hide();
50223                 }
50224             }
50225             /** This region's collapsed element
50226             * @type Roo.Element */
50227             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50228                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50229             ]}, true);
50230             if(c.floatable !== false){
50231                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50232                this.collapsedEl.on("click", this.collapseClick, this);
50233             }
50234
50235             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50236                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50237                    id: "message", unselectable: "on", style:{"float":"left"}});
50238                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50239              }
50240             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50241             this.expandBtn.on("click", this.expand, this);
50242         }
50243         if(this.collapseBtn){
50244             this.collapseBtn.setVisible(c.collapsible == true);
50245         }
50246         this.cmargins = c.cmargins || this.cmargins ||
50247                          (this.position == "west" || this.position == "east" ?
50248                              {top: 0, left: 2, right:2, bottom: 0} :
50249                              {top: 2, left: 0, right:0, bottom: 2});
50250         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50251         this.bottomTabs = c.tabPosition != "top";
50252         this.autoScroll = c.autoScroll || false;
50253         if(this.autoScroll){
50254             this.bodyEl.setStyle("overflow", "auto");
50255         }else{
50256             this.bodyEl.setStyle("overflow", "hidden");
50257         }
50258         //if(c.titlebar !== false){
50259             if((!c.titlebar && !c.title) || c.titlebar === false){
50260                 this.titleEl.hide();
50261             }else{
50262                 this.titleEl.show();
50263                 if(c.title){
50264                     this.titleTextEl.innerHTML = c.title;
50265                 }
50266             }
50267         //}
50268         this.duration = c.duration || .30;
50269         this.slideDuration = c.slideDuration || .45;
50270         this.config = c;
50271         if(c.collapsed){
50272             this.collapse(true);
50273         }
50274         if(c.hidden){
50275             this.hide();
50276         }
50277     },
50278     /**
50279      * Returns true if this region is currently visible.
50280      * @return {Boolean}
50281      */
50282     isVisible : function(){
50283         return this.visible;
50284     },
50285
50286     /**
50287      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50288      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50289      */
50290     setCollapsedTitle : function(title){
50291         title = title || "&#160;";
50292         if(this.collapsedTitleTextEl){
50293             this.collapsedTitleTextEl.innerHTML = title;
50294         }
50295     },
50296
50297     getBox : function(){
50298         var b;
50299         if(!this.collapsed){
50300             b = this.el.getBox(false, true);
50301         }else{
50302             b = this.collapsedEl.getBox(false, true);
50303         }
50304         return b;
50305     },
50306
50307     getMargins : function(){
50308         return this.collapsed ? this.cmargins : this.margins;
50309     },
50310
50311     highlight : function(){
50312         this.el.addClass("x-layout-panel-dragover");
50313     },
50314
50315     unhighlight : function(){
50316         this.el.removeClass("x-layout-panel-dragover");
50317     },
50318
50319     updateBox : function(box){
50320         this.box = box;
50321         if(!this.collapsed){
50322             this.el.dom.style.left = box.x + "px";
50323             this.el.dom.style.top = box.y + "px";
50324             this.updateBody(box.width, box.height);
50325         }else{
50326             this.collapsedEl.dom.style.left = box.x + "px";
50327             this.collapsedEl.dom.style.top = box.y + "px";
50328             this.collapsedEl.setSize(box.width, box.height);
50329         }
50330         if(this.tabs){
50331             this.tabs.autoSizeTabs();
50332         }
50333     },
50334
50335     updateBody : function(w, h){
50336         if(w !== null){
50337             this.el.setWidth(w);
50338             w -= this.el.getBorderWidth("rl");
50339             if(this.config.adjustments){
50340                 w += this.config.adjustments[0];
50341             }
50342         }
50343         if(h !== null){
50344             this.el.setHeight(h);
50345             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50346             h -= this.el.getBorderWidth("tb");
50347             if(this.config.adjustments){
50348                 h += this.config.adjustments[1];
50349             }
50350             this.bodyEl.setHeight(h);
50351             if(this.tabs){
50352                 h = this.tabs.syncHeight(h);
50353             }
50354         }
50355         if(this.panelSize){
50356             w = w !== null ? w : this.panelSize.width;
50357             h = h !== null ? h : this.panelSize.height;
50358         }
50359         if(this.activePanel){
50360             var el = this.activePanel.getEl();
50361             w = w !== null ? w : el.getWidth();
50362             h = h !== null ? h : el.getHeight();
50363             this.panelSize = {width: w, height: h};
50364             this.activePanel.setSize(w, h);
50365         }
50366         if(Roo.isIE && this.tabs){
50367             this.tabs.el.repaint();
50368         }
50369     },
50370
50371     /**
50372      * Returns the container element for this region.
50373      * @return {Roo.Element}
50374      */
50375     getEl : function(){
50376         return this.el;
50377     },
50378
50379     /**
50380      * Hides this region.
50381      */
50382     hide : function(){
50383         if(!this.collapsed){
50384             this.el.dom.style.left = "-2000px";
50385             this.el.hide();
50386         }else{
50387             this.collapsedEl.dom.style.left = "-2000px";
50388             this.collapsedEl.hide();
50389         }
50390         this.visible = false;
50391         this.fireEvent("visibilitychange", this, false);
50392     },
50393
50394     /**
50395      * Shows this region if it was previously hidden.
50396      */
50397     show : function(){
50398         if(!this.collapsed){
50399             this.el.show();
50400         }else{
50401             this.collapsedEl.show();
50402         }
50403         this.visible = true;
50404         this.fireEvent("visibilitychange", this, true);
50405     },
50406
50407     closeClicked : function(){
50408         if(this.activePanel){
50409             this.remove(this.activePanel);
50410         }
50411     },
50412
50413     collapseClick : function(e){
50414         if(this.isSlid){
50415            e.stopPropagation();
50416            this.slideIn();
50417         }else{
50418            e.stopPropagation();
50419            this.slideOut();
50420         }
50421     },
50422
50423     /**
50424      * Collapses this region.
50425      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50426      */
50427     collapse : function(skipAnim){
50428         if(this.collapsed) return;
50429         this.collapsed = true;
50430         if(this.split){
50431             this.split.el.hide();
50432         }
50433         if(this.config.animate && skipAnim !== true){
50434             this.fireEvent("invalidated", this);
50435             this.animateCollapse();
50436         }else{
50437             this.el.setLocation(-20000,-20000);
50438             this.el.hide();
50439             this.collapsedEl.show();
50440             this.fireEvent("collapsed", this);
50441             this.fireEvent("invalidated", this);
50442         }
50443     },
50444
50445     animateCollapse : function(){
50446         // overridden
50447     },
50448
50449     /**
50450      * Expands this region if it was previously collapsed.
50451      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50452      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50453      */
50454     expand : function(e, skipAnim){
50455         if(e) e.stopPropagation();
50456         if(!this.collapsed || this.el.hasActiveFx()) return;
50457         if(this.isSlid){
50458             this.afterSlideIn();
50459             skipAnim = true;
50460         }
50461         this.collapsed = false;
50462         if(this.config.animate && skipAnim !== true){
50463             this.animateExpand();
50464         }else{
50465             this.el.show();
50466             if(this.split){
50467                 this.split.el.show();
50468             }
50469             this.collapsedEl.setLocation(-2000,-2000);
50470             this.collapsedEl.hide();
50471             this.fireEvent("invalidated", this);
50472             this.fireEvent("expanded", this);
50473         }
50474     },
50475
50476     animateExpand : function(){
50477         // overridden
50478     },
50479
50480     initTabs : function()
50481     {
50482         this.bodyEl.setStyle("overflow", "hidden");
50483         var ts = new Roo.TabPanel(
50484                 this.bodyEl.dom,
50485                 {
50486                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50487                     disableTooltips: this.config.disableTabTips,
50488                     toolbar : this.config.toolbar
50489                 }
50490         );
50491         if(this.config.hideTabs){
50492             ts.stripWrap.setDisplayed(false);
50493         }
50494         this.tabs = ts;
50495         ts.resizeTabs = this.config.resizeTabs === true;
50496         ts.minTabWidth = this.config.minTabWidth || 40;
50497         ts.maxTabWidth = this.config.maxTabWidth || 250;
50498         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50499         ts.monitorResize = false;
50500         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50501         ts.bodyEl.addClass('x-layout-tabs-body');
50502         this.panels.each(this.initPanelAsTab, this);
50503     },
50504
50505     initPanelAsTab : function(panel){
50506         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50507                     this.config.closeOnTab && panel.isClosable());
50508         if(panel.tabTip !== undefined){
50509             ti.setTooltip(panel.tabTip);
50510         }
50511         ti.on("activate", function(){
50512               this.setActivePanel(panel);
50513         }, this);
50514         if(this.config.closeOnTab){
50515             ti.on("beforeclose", function(t, e){
50516                 e.cancel = true;
50517                 this.remove(panel);
50518             }, this);
50519         }
50520         return ti;
50521     },
50522
50523     updatePanelTitle : function(panel, title){
50524         if(this.activePanel == panel){
50525             this.updateTitle(title);
50526         }
50527         if(this.tabs){
50528             var ti = this.tabs.getTab(panel.getEl().id);
50529             ti.setText(title);
50530             if(panel.tabTip !== undefined){
50531                 ti.setTooltip(panel.tabTip);
50532             }
50533         }
50534     },
50535
50536     updateTitle : function(title){
50537         if(this.titleTextEl && !this.config.title){
50538             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50539         }
50540     },
50541
50542     setActivePanel : function(panel){
50543         panel = this.getPanel(panel);
50544         if(this.activePanel && this.activePanel != panel){
50545             this.activePanel.setActiveState(false);
50546         }
50547         this.activePanel = panel;
50548         panel.setActiveState(true);
50549         if(this.panelSize){
50550             panel.setSize(this.panelSize.width, this.panelSize.height);
50551         }
50552         if(this.closeBtn){
50553             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50554         }
50555         this.updateTitle(panel.getTitle());
50556         if(this.tabs){
50557             this.fireEvent("invalidated", this);
50558         }
50559         this.fireEvent("panelactivated", this, panel);
50560     },
50561
50562     /**
50563      * Shows the specified panel.
50564      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50565      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50566      */
50567     showPanel : function(panel)
50568     {
50569         panel = this.getPanel(panel);
50570         if(panel){
50571             if(this.tabs){
50572                 var tab = this.tabs.getTab(panel.getEl().id);
50573                 if(tab.isHidden()){
50574                     this.tabs.unhideTab(tab.id);
50575                 }
50576                 tab.activate();
50577             }else{
50578                 this.setActivePanel(panel);
50579             }
50580         }
50581         return panel;
50582     },
50583
50584     /**
50585      * Get the active panel for this region.
50586      * @return {Roo.ContentPanel} The active panel or null
50587      */
50588     getActivePanel : function(){
50589         return this.activePanel;
50590     },
50591
50592     validateVisibility : function(){
50593         if(this.panels.getCount() < 1){
50594             this.updateTitle("&#160;");
50595             this.closeBtn.hide();
50596             this.hide();
50597         }else{
50598             if(!this.isVisible()){
50599                 this.show();
50600             }
50601         }
50602     },
50603
50604     /**
50605      * Adds the passed ContentPanel(s) to this region.
50606      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50607      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50608      */
50609     add : function(panel){
50610         if(arguments.length > 1){
50611             for(var i = 0, len = arguments.length; i < len; i++) {
50612                 this.add(arguments[i]);
50613             }
50614             return null;
50615         }
50616         if(this.hasPanel(panel)){
50617             this.showPanel(panel);
50618             return panel;
50619         }
50620         panel.setRegion(this);
50621         this.panels.add(panel);
50622         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50623             this.bodyEl.dom.appendChild(panel.getEl().dom);
50624             if(panel.background !== true){
50625                 this.setActivePanel(panel);
50626             }
50627             this.fireEvent("paneladded", this, panel);
50628             return panel;
50629         }
50630         if(!this.tabs){
50631             this.initTabs();
50632         }else{
50633             this.initPanelAsTab(panel);
50634         }
50635         if(panel.background !== true){
50636             this.tabs.activate(panel.getEl().id);
50637         }
50638         this.fireEvent("paneladded", this, panel);
50639         return panel;
50640     },
50641
50642     /**
50643      * Hides the tab for the specified panel.
50644      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50645      */
50646     hidePanel : function(panel){
50647         if(this.tabs && (panel = this.getPanel(panel))){
50648             this.tabs.hideTab(panel.getEl().id);
50649         }
50650     },
50651
50652     /**
50653      * Unhides the tab for a previously hidden panel.
50654      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50655      */
50656     unhidePanel : function(panel){
50657         if(this.tabs && (panel = this.getPanel(panel))){
50658             this.tabs.unhideTab(panel.getEl().id);
50659         }
50660     },
50661
50662     clearPanels : function(){
50663         while(this.panels.getCount() > 0){
50664              this.remove(this.panels.first());
50665         }
50666     },
50667
50668     /**
50669      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50670      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50671      * @param {Boolean} preservePanel Overrides the config preservePanel option
50672      * @return {Roo.ContentPanel} The panel that was removed
50673      */
50674     remove : function(panel, preservePanel){
50675         panel = this.getPanel(panel);
50676         if(!panel){
50677             return null;
50678         }
50679         var e = {};
50680         this.fireEvent("beforeremove", this, panel, e);
50681         if(e.cancel === true){
50682             return null;
50683         }
50684         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50685         var panelId = panel.getId();
50686         this.panels.removeKey(panelId);
50687         if(preservePanel){
50688             document.body.appendChild(panel.getEl().dom);
50689         }
50690         if(this.tabs){
50691             this.tabs.removeTab(panel.getEl().id);
50692         }else if (!preservePanel){
50693             this.bodyEl.dom.removeChild(panel.getEl().dom);
50694         }
50695         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50696             var p = this.panels.first();
50697             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50698             tempEl.appendChild(p.getEl().dom);
50699             this.bodyEl.update("");
50700             this.bodyEl.dom.appendChild(p.getEl().dom);
50701             tempEl = null;
50702             this.updateTitle(p.getTitle());
50703             this.tabs = null;
50704             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50705             this.setActivePanel(p);
50706         }
50707         panel.setRegion(null);
50708         if(this.activePanel == panel){
50709             this.activePanel = null;
50710         }
50711         if(this.config.autoDestroy !== false && preservePanel !== true){
50712             try{panel.destroy();}catch(e){}
50713         }
50714         this.fireEvent("panelremoved", this, panel);
50715         return panel;
50716     },
50717
50718     /**
50719      * Returns the TabPanel component used by this region
50720      * @return {Roo.TabPanel}
50721      */
50722     getTabs : function(){
50723         return this.tabs;
50724     },
50725
50726     createTool : function(parentEl, className){
50727         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50728             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50729         btn.addClassOnOver("x-layout-tools-button-over");
50730         return btn;
50731     }
50732 });/*
50733  * Based on:
50734  * Ext JS Library 1.1.1
50735  * Copyright(c) 2006-2007, Ext JS, LLC.
50736  *
50737  * Originally Released Under LGPL - original licence link has changed is not relivant.
50738  *
50739  * Fork - LGPL
50740  * <script type="text/javascript">
50741  */
50742  
50743
50744
50745 /**
50746  * @class Roo.SplitLayoutRegion
50747  * @extends Roo.LayoutRegion
50748  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50749  */
50750 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50751     this.cursor = cursor;
50752     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50753 };
50754
50755 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50756     splitTip : "Drag to resize.",
50757     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50758     useSplitTips : false,
50759
50760     applyConfig : function(config){
50761         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50762         if(config.split){
50763             if(!this.split){
50764                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50765                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50766                 /** The SplitBar for this region 
50767                 * @type Roo.SplitBar */
50768                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50769                 this.split.on("moved", this.onSplitMove, this);
50770                 this.split.useShim = config.useShim === true;
50771                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50772                 if(this.useSplitTips){
50773                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50774                 }
50775                 if(config.collapsible){
50776                     this.split.el.on("dblclick", this.collapse,  this);
50777                 }
50778             }
50779             if(typeof config.minSize != "undefined"){
50780                 this.split.minSize = config.minSize;
50781             }
50782             if(typeof config.maxSize != "undefined"){
50783                 this.split.maxSize = config.maxSize;
50784             }
50785             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50786                 this.hideSplitter();
50787             }
50788         }
50789     },
50790
50791     getHMaxSize : function(){
50792          var cmax = this.config.maxSize || 10000;
50793          var center = this.mgr.getRegion("center");
50794          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50795     },
50796
50797     getVMaxSize : function(){
50798          var cmax = this.config.maxSize || 10000;
50799          var center = this.mgr.getRegion("center");
50800          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50801     },
50802
50803     onSplitMove : function(split, newSize){
50804         this.fireEvent("resized", this, newSize);
50805     },
50806     
50807     /** 
50808      * Returns the {@link Roo.SplitBar} for this region.
50809      * @return {Roo.SplitBar}
50810      */
50811     getSplitBar : function(){
50812         return this.split;
50813     },
50814     
50815     hide : function(){
50816         this.hideSplitter();
50817         Roo.SplitLayoutRegion.superclass.hide.call(this);
50818     },
50819
50820     hideSplitter : function(){
50821         if(this.split){
50822             this.split.el.setLocation(-2000,-2000);
50823             this.split.el.hide();
50824         }
50825     },
50826
50827     show : function(){
50828         if(this.split){
50829             this.split.el.show();
50830         }
50831         Roo.SplitLayoutRegion.superclass.show.call(this);
50832     },
50833     
50834     beforeSlide: function(){
50835         if(Roo.isGecko){// firefox overflow auto bug workaround
50836             this.bodyEl.clip();
50837             if(this.tabs) this.tabs.bodyEl.clip();
50838             if(this.activePanel){
50839                 this.activePanel.getEl().clip();
50840                 
50841                 if(this.activePanel.beforeSlide){
50842                     this.activePanel.beforeSlide();
50843                 }
50844             }
50845         }
50846     },
50847     
50848     afterSlide : function(){
50849         if(Roo.isGecko){// firefox overflow auto bug workaround
50850             this.bodyEl.unclip();
50851             if(this.tabs) this.tabs.bodyEl.unclip();
50852             if(this.activePanel){
50853                 this.activePanel.getEl().unclip();
50854                 if(this.activePanel.afterSlide){
50855                     this.activePanel.afterSlide();
50856                 }
50857             }
50858         }
50859     },
50860
50861     initAutoHide : function(){
50862         if(this.autoHide !== false){
50863             if(!this.autoHideHd){
50864                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50865                 this.autoHideHd = {
50866                     "mouseout": function(e){
50867                         if(!e.within(this.el, true)){
50868                             st.delay(500);
50869                         }
50870                     },
50871                     "mouseover" : function(e){
50872                         st.cancel();
50873                     },
50874                     scope : this
50875                 };
50876             }
50877             this.el.on(this.autoHideHd);
50878         }
50879     },
50880
50881     clearAutoHide : function(){
50882         if(this.autoHide !== false){
50883             this.el.un("mouseout", this.autoHideHd.mouseout);
50884             this.el.un("mouseover", this.autoHideHd.mouseover);
50885         }
50886     },
50887
50888     clearMonitor : function(){
50889         Roo.get(document).un("click", this.slideInIf, this);
50890     },
50891
50892     // these names are backwards but not changed for compat
50893     slideOut : function(){
50894         if(this.isSlid || this.el.hasActiveFx()){
50895             return;
50896         }
50897         this.isSlid = true;
50898         if(this.collapseBtn){
50899             this.collapseBtn.hide();
50900         }
50901         this.closeBtnState = this.closeBtn.getStyle('display');
50902         this.closeBtn.hide();
50903         if(this.stickBtn){
50904             this.stickBtn.show();
50905         }
50906         this.el.show();
50907         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50908         this.beforeSlide();
50909         this.el.setStyle("z-index", 10001);
50910         this.el.slideIn(this.getSlideAnchor(), {
50911             callback: function(){
50912                 this.afterSlide();
50913                 this.initAutoHide();
50914                 Roo.get(document).on("click", this.slideInIf, this);
50915                 this.fireEvent("slideshow", this);
50916             },
50917             scope: this,
50918             block: true
50919         });
50920     },
50921
50922     afterSlideIn : function(){
50923         this.clearAutoHide();
50924         this.isSlid = false;
50925         this.clearMonitor();
50926         this.el.setStyle("z-index", "");
50927         if(this.collapseBtn){
50928             this.collapseBtn.show();
50929         }
50930         this.closeBtn.setStyle('display', this.closeBtnState);
50931         if(this.stickBtn){
50932             this.stickBtn.hide();
50933         }
50934         this.fireEvent("slidehide", this);
50935     },
50936
50937     slideIn : function(cb){
50938         if(!this.isSlid || this.el.hasActiveFx()){
50939             Roo.callback(cb);
50940             return;
50941         }
50942         this.isSlid = false;
50943         this.beforeSlide();
50944         this.el.slideOut(this.getSlideAnchor(), {
50945             callback: function(){
50946                 this.el.setLeftTop(-10000, -10000);
50947                 this.afterSlide();
50948                 this.afterSlideIn();
50949                 Roo.callback(cb);
50950             },
50951             scope: this,
50952             block: true
50953         });
50954     },
50955     
50956     slideInIf : function(e){
50957         if(!e.within(this.el)){
50958             this.slideIn();
50959         }
50960     },
50961
50962     animateCollapse : function(){
50963         this.beforeSlide();
50964         this.el.setStyle("z-index", 20000);
50965         var anchor = this.getSlideAnchor();
50966         this.el.slideOut(anchor, {
50967             callback : function(){
50968                 this.el.setStyle("z-index", "");
50969                 this.collapsedEl.slideIn(anchor, {duration:.3});
50970                 this.afterSlide();
50971                 this.el.setLocation(-10000,-10000);
50972                 this.el.hide();
50973                 this.fireEvent("collapsed", this);
50974             },
50975             scope: this,
50976             block: true
50977         });
50978     },
50979
50980     animateExpand : function(){
50981         this.beforeSlide();
50982         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50983         this.el.setStyle("z-index", 20000);
50984         this.collapsedEl.hide({
50985             duration:.1
50986         });
50987         this.el.slideIn(this.getSlideAnchor(), {
50988             callback : function(){
50989                 this.el.setStyle("z-index", "");
50990                 this.afterSlide();
50991                 if(this.split){
50992                     this.split.el.show();
50993                 }
50994                 this.fireEvent("invalidated", this);
50995                 this.fireEvent("expanded", this);
50996             },
50997             scope: this,
50998             block: true
50999         });
51000     },
51001
51002     anchors : {
51003         "west" : "left",
51004         "east" : "right",
51005         "north" : "top",
51006         "south" : "bottom"
51007     },
51008
51009     sanchors : {
51010         "west" : "l",
51011         "east" : "r",
51012         "north" : "t",
51013         "south" : "b"
51014     },
51015
51016     canchors : {
51017         "west" : "tl-tr",
51018         "east" : "tr-tl",
51019         "north" : "tl-bl",
51020         "south" : "bl-tl"
51021     },
51022
51023     getAnchor : function(){
51024         return this.anchors[this.position];
51025     },
51026
51027     getCollapseAnchor : function(){
51028         return this.canchors[this.position];
51029     },
51030
51031     getSlideAnchor : function(){
51032         return this.sanchors[this.position];
51033     },
51034
51035     getAlignAdj : function(){
51036         var cm = this.cmargins;
51037         switch(this.position){
51038             case "west":
51039                 return [0, 0];
51040             break;
51041             case "east":
51042                 return [0, 0];
51043             break;
51044             case "north":
51045                 return [0, 0];
51046             break;
51047             case "south":
51048                 return [0, 0];
51049             break;
51050         }
51051     },
51052
51053     getExpandAdj : function(){
51054         var c = this.collapsedEl, cm = this.cmargins;
51055         switch(this.position){
51056             case "west":
51057                 return [-(cm.right+c.getWidth()+cm.left), 0];
51058             break;
51059             case "east":
51060                 return [cm.right+c.getWidth()+cm.left, 0];
51061             break;
51062             case "north":
51063                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51064             break;
51065             case "south":
51066                 return [0, cm.top+cm.bottom+c.getHeight()];
51067             break;
51068         }
51069     }
51070 });/*
51071  * Based on:
51072  * Ext JS Library 1.1.1
51073  * Copyright(c) 2006-2007, Ext JS, LLC.
51074  *
51075  * Originally Released Under LGPL - original licence link has changed is not relivant.
51076  *
51077  * Fork - LGPL
51078  * <script type="text/javascript">
51079  */
51080 /*
51081  * These classes are private internal classes
51082  */
51083 Roo.CenterLayoutRegion = function(mgr, config){
51084     Roo.LayoutRegion.call(this, mgr, config, "center");
51085     this.visible = true;
51086     this.minWidth = config.minWidth || 20;
51087     this.minHeight = config.minHeight || 20;
51088 };
51089
51090 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51091     hide : function(){
51092         // center panel can't be hidden
51093     },
51094     
51095     show : function(){
51096         // center panel can't be hidden
51097     },
51098     
51099     getMinWidth: function(){
51100         return this.minWidth;
51101     },
51102     
51103     getMinHeight: function(){
51104         return this.minHeight;
51105     }
51106 });
51107
51108
51109 Roo.NorthLayoutRegion = function(mgr, config){
51110     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51111     if(this.split){
51112         this.split.placement = Roo.SplitBar.TOP;
51113         this.split.orientation = Roo.SplitBar.VERTICAL;
51114         this.split.el.addClass("x-layout-split-v");
51115     }
51116     var size = config.initialSize || config.height;
51117     if(typeof size != "undefined"){
51118         this.el.setHeight(size);
51119     }
51120 };
51121 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51122     orientation: Roo.SplitBar.VERTICAL,
51123     getBox : function(){
51124         if(this.collapsed){
51125             return this.collapsedEl.getBox();
51126         }
51127         var box = this.el.getBox();
51128         if(this.split){
51129             box.height += this.split.el.getHeight();
51130         }
51131         return box;
51132     },
51133     
51134     updateBox : function(box){
51135         if(this.split && !this.collapsed){
51136             box.height -= this.split.el.getHeight();
51137             this.split.el.setLeft(box.x);
51138             this.split.el.setTop(box.y+box.height);
51139             this.split.el.setWidth(box.width);
51140         }
51141         if(this.collapsed){
51142             this.updateBody(box.width, null);
51143         }
51144         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51145     }
51146 });
51147
51148 Roo.SouthLayoutRegion = function(mgr, config){
51149     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51150     if(this.split){
51151         this.split.placement = Roo.SplitBar.BOTTOM;
51152         this.split.orientation = Roo.SplitBar.VERTICAL;
51153         this.split.el.addClass("x-layout-split-v");
51154     }
51155     var size = config.initialSize || config.height;
51156     if(typeof size != "undefined"){
51157         this.el.setHeight(size);
51158     }
51159 };
51160 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51161     orientation: Roo.SplitBar.VERTICAL,
51162     getBox : function(){
51163         if(this.collapsed){
51164             return this.collapsedEl.getBox();
51165         }
51166         var box = this.el.getBox();
51167         if(this.split){
51168             var sh = this.split.el.getHeight();
51169             box.height += sh;
51170             box.y -= sh;
51171         }
51172         return box;
51173     },
51174     
51175     updateBox : function(box){
51176         if(this.split && !this.collapsed){
51177             var sh = this.split.el.getHeight();
51178             box.height -= sh;
51179             box.y += sh;
51180             this.split.el.setLeft(box.x);
51181             this.split.el.setTop(box.y-sh);
51182             this.split.el.setWidth(box.width);
51183         }
51184         if(this.collapsed){
51185             this.updateBody(box.width, null);
51186         }
51187         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51188     }
51189 });
51190
51191 Roo.EastLayoutRegion = function(mgr, config){
51192     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51193     if(this.split){
51194         this.split.placement = Roo.SplitBar.RIGHT;
51195         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51196         this.split.el.addClass("x-layout-split-h");
51197     }
51198     var size = config.initialSize || config.width;
51199     if(typeof size != "undefined"){
51200         this.el.setWidth(size);
51201     }
51202 };
51203 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51204     orientation: Roo.SplitBar.HORIZONTAL,
51205     getBox : function(){
51206         if(this.collapsed){
51207             return this.collapsedEl.getBox();
51208         }
51209         var box = this.el.getBox();
51210         if(this.split){
51211             var sw = this.split.el.getWidth();
51212             box.width += sw;
51213             box.x -= sw;
51214         }
51215         return box;
51216     },
51217
51218     updateBox : function(box){
51219         if(this.split && !this.collapsed){
51220             var sw = this.split.el.getWidth();
51221             box.width -= sw;
51222             this.split.el.setLeft(box.x);
51223             this.split.el.setTop(box.y);
51224             this.split.el.setHeight(box.height);
51225             box.x += sw;
51226         }
51227         if(this.collapsed){
51228             this.updateBody(null, box.height);
51229         }
51230         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51231     }
51232 });
51233
51234 Roo.WestLayoutRegion = function(mgr, config){
51235     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51236     if(this.split){
51237         this.split.placement = Roo.SplitBar.LEFT;
51238         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51239         this.split.el.addClass("x-layout-split-h");
51240     }
51241     var size = config.initialSize || config.width;
51242     if(typeof size != "undefined"){
51243         this.el.setWidth(size);
51244     }
51245 };
51246 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51247     orientation: Roo.SplitBar.HORIZONTAL,
51248     getBox : function(){
51249         if(this.collapsed){
51250             return this.collapsedEl.getBox();
51251         }
51252         var box = this.el.getBox();
51253         if(this.split){
51254             box.width += this.split.el.getWidth();
51255         }
51256         return box;
51257     },
51258     
51259     updateBox : function(box){
51260         if(this.split && !this.collapsed){
51261             var sw = this.split.el.getWidth();
51262             box.width -= sw;
51263             this.split.el.setLeft(box.x+box.width);
51264             this.split.el.setTop(box.y);
51265             this.split.el.setHeight(box.height);
51266         }
51267         if(this.collapsed){
51268             this.updateBody(null, box.height);
51269         }
51270         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51271     }
51272 });
51273 /*
51274  * Based on:
51275  * Ext JS Library 1.1.1
51276  * Copyright(c) 2006-2007, Ext JS, LLC.
51277  *
51278  * Originally Released Under LGPL - original licence link has changed is not relivant.
51279  *
51280  * Fork - LGPL
51281  * <script type="text/javascript">
51282  */
51283  
51284  
51285 /*
51286  * Private internal class for reading and applying state
51287  */
51288 Roo.LayoutStateManager = function(layout){
51289      // default empty state
51290      this.state = {
51291         north: {},
51292         south: {},
51293         east: {},
51294         west: {}       
51295     };
51296 };
51297
51298 Roo.LayoutStateManager.prototype = {
51299     init : function(layout, provider){
51300         this.provider = provider;
51301         var state = provider.get(layout.id+"-layout-state");
51302         if(state){
51303             var wasUpdating = layout.isUpdating();
51304             if(!wasUpdating){
51305                 layout.beginUpdate();
51306             }
51307             for(var key in state){
51308                 if(typeof state[key] != "function"){
51309                     var rstate = state[key];
51310                     var r = layout.getRegion(key);
51311                     if(r && rstate){
51312                         if(rstate.size){
51313                             r.resizeTo(rstate.size);
51314                         }
51315                         if(rstate.collapsed == true){
51316                             r.collapse(true);
51317                         }else{
51318                             r.expand(null, true);
51319                         }
51320                     }
51321                 }
51322             }
51323             if(!wasUpdating){
51324                 layout.endUpdate();
51325             }
51326             this.state = state; 
51327         }
51328         this.layout = layout;
51329         layout.on("regionresized", this.onRegionResized, this);
51330         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51331         layout.on("regionexpanded", this.onRegionExpanded, this);
51332     },
51333     
51334     storeState : function(){
51335         this.provider.set(this.layout.id+"-layout-state", this.state);
51336     },
51337     
51338     onRegionResized : function(region, newSize){
51339         this.state[region.getPosition()].size = newSize;
51340         this.storeState();
51341     },
51342     
51343     onRegionCollapsed : function(region){
51344         this.state[region.getPosition()].collapsed = true;
51345         this.storeState();
51346     },
51347     
51348     onRegionExpanded : function(region){
51349         this.state[region.getPosition()].collapsed = false;
51350         this.storeState();
51351     }
51352 };/*
51353  * Based on:
51354  * Ext JS Library 1.1.1
51355  * Copyright(c) 2006-2007, Ext JS, LLC.
51356  *
51357  * Originally Released Under LGPL - original licence link has changed is not relivant.
51358  *
51359  * Fork - LGPL
51360  * <script type="text/javascript">
51361  */
51362 /**
51363  * @class Roo.ContentPanel
51364  * @extends Roo.util.Observable
51365  * A basic ContentPanel element.
51366  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51367  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51368  * @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
51369  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51370  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51371  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51372  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51373  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51374  * @cfg {String} title          The title for this panel
51375  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51376  * @cfg {String} url            Calls {@link #setUrl} with this value
51377  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51378  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51379  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51380  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51381
51382  * @constructor
51383  * Create a new ContentPanel.
51384  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51385  * @param {String/Object} config A string to set only the title or a config object
51386  * @param {String} content (optional) Set the HTML content for this panel
51387  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51388  */
51389 Roo.ContentPanel = function(el, config, content){
51390     
51391      
51392     /*
51393     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51394         config = el;
51395         el = Roo.id();
51396     }
51397     if (config && config.parentLayout) { 
51398         el = config.parentLayout.el.createChild(); 
51399     }
51400     */
51401     if(el.autoCreate){ // xtype is available if this is called from factory
51402         config = el;
51403         el = Roo.id();
51404     }
51405     this.el = Roo.get(el);
51406     if(!this.el && config && config.autoCreate){
51407         if(typeof config.autoCreate == "object"){
51408             if(!config.autoCreate.id){
51409                 config.autoCreate.id = config.id||el;
51410             }
51411             this.el = Roo.DomHelper.append(document.body,
51412                         config.autoCreate, true);
51413         }else{
51414             this.el = Roo.DomHelper.append(document.body,
51415                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51416         }
51417     }
51418     this.closable = false;
51419     this.loaded = false;
51420     this.active = false;
51421     if(typeof config == "string"){
51422         this.title = config;
51423     }else{
51424         Roo.apply(this, config);
51425     }
51426     
51427     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51428         this.wrapEl = this.el.wrap();
51429         this.toolbar.container = this.el.insertSibling(false, 'before');
51430         this.toolbar = new Roo.Toolbar(this.toolbar);
51431     }
51432     
51433     // xtype created footer. - not sure if will work as we normally have to render first..
51434     if (this.footer && !this.footer.el && this.footer.xtype) {
51435         if (!this.wrapEl) {
51436             this.wrapEl = this.el.wrap();
51437         }
51438     
51439         this.footer.container = this.wrapEl.createChild();
51440          
51441         this.footer = Roo.factory(this.footer, Roo);
51442         
51443     }
51444     
51445     if(this.resizeEl){
51446         this.resizeEl = Roo.get(this.resizeEl, true);
51447     }else{
51448         this.resizeEl = this.el;
51449     }
51450     // handle view.xtype
51451     
51452  
51453     
51454     
51455     this.addEvents({
51456         /**
51457          * @event activate
51458          * Fires when this panel is activated. 
51459          * @param {Roo.ContentPanel} this
51460          */
51461         "activate" : true,
51462         /**
51463          * @event deactivate
51464          * Fires when this panel is activated. 
51465          * @param {Roo.ContentPanel} this
51466          */
51467         "deactivate" : true,
51468
51469         /**
51470          * @event resize
51471          * Fires when this panel is resized if fitToFrame is true.
51472          * @param {Roo.ContentPanel} this
51473          * @param {Number} width The width after any component adjustments
51474          * @param {Number} height The height after any component adjustments
51475          */
51476         "resize" : true,
51477         
51478          /**
51479          * @event render
51480          * Fires when this tab is created
51481          * @param {Roo.ContentPanel} this
51482          */
51483         "render" : true
51484         
51485         
51486         
51487     });
51488     
51489
51490     
51491     
51492     if(this.autoScroll){
51493         this.resizeEl.setStyle("overflow", "auto");
51494     } else {
51495         // fix randome scrolling
51496         this.el.on('scroll', function() {
51497             Roo.log('fix random scolling');
51498             this.scrollTo('top',0); 
51499         });
51500     }
51501     content = content || this.content;
51502     if(content){
51503         this.setContent(content);
51504     }
51505     if(config && config.url){
51506         this.setUrl(this.url, this.params, this.loadOnce);
51507     }
51508     
51509     
51510     
51511     Roo.ContentPanel.superclass.constructor.call(this);
51512     
51513     if (this.view && typeof(this.view.xtype) != 'undefined') {
51514         this.view.el = this.el.appendChild(document.createElement("div"));
51515         this.view = Roo.factory(this.view); 
51516         this.view.render  &&  this.view.render(false, '');  
51517     }
51518     
51519     
51520     this.fireEvent('render', this);
51521 };
51522
51523 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51524     tabTip:'',
51525     setRegion : function(region){
51526         this.region = region;
51527         if(region){
51528            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51529         }else{
51530            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51531         } 
51532     },
51533     
51534     /**
51535      * Returns the toolbar for this Panel if one was configured. 
51536      * @return {Roo.Toolbar} 
51537      */
51538     getToolbar : function(){
51539         return this.toolbar;
51540     },
51541     
51542     setActiveState : function(active){
51543         this.active = active;
51544         if(!active){
51545             this.fireEvent("deactivate", this);
51546         }else{
51547             this.fireEvent("activate", this);
51548         }
51549     },
51550     /**
51551      * Updates this panel's element
51552      * @param {String} content The new content
51553      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51554     */
51555     setContent : function(content, loadScripts){
51556         this.el.update(content, loadScripts);
51557     },
51558
51559     ignoreResize : function(w, h){
51560         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51561             return true;
51562         }else{
51563             this.lastSize = {width: w, height: h};
51564             return false;
51565         }
51566     },
51567     /**
51568      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51569      * @return {Roo.UpdateManager} The UpdateManager
51570      */
51571     getUpdateManager : function(){
51572         return this.el.getUpdateManager();
51573     },
51574      /**
51575      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51576      * @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:
51577 <pre><code>
51578 panel.load({
51579     url: "your-url.php",
51580     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51581     callback: yourFunction,
51582     scope: yourObject, //(optional scope)
51583     discardUrl: false,
51584     nocache: false,
51585     text: "Loading...",
51586     timeout: 30,
51587     scripts: false
51588 });
51589 </code></pre>
51590      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51591      * 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.
51592      * @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}
51593      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51594      * @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.
51595      * @return {Roo.ContentPanel} this
51596      */
51597     load : function(){
51598         var um = this.el.getUpdateManager();
51599         um.update.apply(um, arguments);
51600         return this;
51601     },
51602
51603
51604     /**
51605      * 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.
51606      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51607      * @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)
51608      * @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)
51609      * @return {Roo.UpdateManager} The UpdateManager
51610      */
51611     setUrl : function(url, params, loadOnce){
51612         if(this.refreshDelegate){
51613             this.removeListener("activate", this.refreshDelegate);
51614         }
51615         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51616         this.on("activate", this.refreshDelegate);
51617         return this.el.getUpdateManager();
51618     },
51619     
51620     _handleRefresh : function(url, params, loadOnce){
51621         if(!loadOnce || !this.loaded){
51622             var updater = this.el.getUpdateManager();
51623             updater.update(url, params, this._setLoaded.createDelegate(this));
51624         }
51625     },
51626     
51627     _setLoaded : function(){
51628         this.loaded = true;
51629     }, 
51630     
51631     /**
51632      * Returns this panel's id
51633      * @return {String} 
51634      */
51635     getId : function(){
51636         return this.el.id;
51637     },
51638     
51639     /** 
51640      * Returns this panel's element - used by regiosn to add.
51641      * @return {Roo.Element} 
51642      */
51643     getEl : function(){
51644         return this.wrapEl || this.el;
51645     },
51646     
51647     adjustForComponents : function(width, height)
51648     {
51649         //Roo.log('adjustForComponents ');
51650         if(this.resizeEl != this.el){
51651             width -= this.el.getFrameWidth('lr');
51652             height -= this.el.getFrameWidth('tb');
51653         }
51654         if(this.toolbar){
51655             var te = this.toolbar.getEl();
51656             height -= te.getHeight();
51657             te.setWidth(width);
51658         }
51659         if(this.footer){
51660             var te = this.footer.getEl();
51661             Roo.log("footer:" + te.getHeight());
51662             
51663             height -= te.getHeight();
51664             te.setWidth(width);
51665         }
51666         
51667         
51668         if(this.adjustments){
51669             width += this.adjustments[0];
51670             height += this.adjustments[1];
51671         }
51672         return {"width": width, "height": height};
51673     },
51674     
51675     setSize : function(width, height){
51676         if(this.fitToFrame && !this.ignoreResize(width, height)){
51677             if(this.fitContainer && this.resizeEl != this.el){
51678                 this.el.setSize(width, height);
51679             }
51680             var size = this.adjustForComponents(width, height);
51681             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51682             this.fireEvent('resize', this, size.width, size.height);
51683         }
51684     },
51685     
51686     /**
51687      * Returns this panel's title
51688      * @return {String} 
51689      */
51690     getTitle : function(){
51691         return this.title;
51692     },
51693     
51694     /**
51695      * Set this panel's title
51696      * @param {String} title
51697      */
51698     setTitle : function(title){
51699         this.title = title;
51700         if(this.region){
51701             this.region.updatePanelTitle(this, title);
51702         }
51703     },
51704     
51705     /**
51706      * Returns true is this panel was configured to be closable
51707      * @return {Boolean} 
51708      */
51709     isClosable : function(){
51710         return this.closable;
51711     },
51712     
51713     beforeSlide : function(){
51714         this.el.clip();
51715         this.resizeEl.clip();
51716     },
51717     
51718     afterSlide : function(){
51719         this.el.unclip();
51720         this.resizeEl.unclip();
51721     },
51722     
51723     /**
51724      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51725      *   Will fail silently if the {@link #setUrl} method has not been called.
51726      *   This does not activate the panel, just updates its content.
51727      */
51728     refresh : function(){
51729         if(this.refreshDelegate){
51730            this.loaded = false;
51731            this.refreshDelegate();
51732         }
51733     },
51734     
51735     /**
51736      * Destroys this panel
51737      */
51738     destroy : function(){
51739         this.el.removeAllListeners();
51740         var tempEl = document.createElement("span");
51741         tempEl.appendChild(this.el.dom);
51742         tempEl.innerHTML = "";
51743         this.el.remove();
51744         this.el = null;
51745     },
51746     
51747     /**
51748      * form - if the content panel contains a form - this is a reference to it.
51749      * @type {Roo.form.Form}
51750      */
51751     form : false,
51752     /**
51753      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51754      *    This contains a reference to it.
51755      * @type {Roo.View}
51756      */
51757     view : false,
51758     
51759       /**
51760      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51761      * <pre><code>
51762
51763 layout.addxtype({
51764        xtype : 'Form',
51765        items: [ .... ]
51766    }
51767 );
51768
51769 </code></pre>
51770      * @param {Object} cfg Xtype definition of item to add.
51771      */
51772     
51773     addxtype : function(cfg) {
51774         // add form..
51775         if (cfg.xtype.match(/^Form$/)) {
51776             
51777             var el;
51778             //if (this.footer) {
51779             //    el = this.footer.container.insertSibling(false, 'before');
51780             //} else {
51781                 el = this.el.createChild();
51782             //}
51783
51784             this.form = new  Roo.form.Form(cfg);
51785             
51786             
51787             if ( this.form.allItems.length) this.form.render(el.dom);
51788             return this.form;
51789         }
51790         // should only have one of theses..
51791         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51792             // views.. should not be just added - used named prop 'view''
51793             
51794             cfg.el = this.el.appendChild(document.createElement("div"));
51795             // factory?
51796             
51797             var ret = new Roo.factory(cfg);
51798              
51799              ret.render && ret.render(false, ''); // render blank..
51800             this.view = ret;
51801             return ret;
51802         }
51803         return false;
51804     }
51805 });
51806
51807 /**
51808  * @class Roo.GridPanel
51809  * @extends Roo.ContentPanel
51810  * @constructor
51811  * Create a new GridPanel.
51812  * @param {Roo.grid.Grid} grid The grid for this panel
51813  * @param {String/Object} config A string to set only the panel's title, or a config object
51814  */
51815 Roo.GridPanel = function(grid, config){
51816     
51817   
51818     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51819         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51820         
51821     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51822     
51823     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51824     
51825     if(this.toolbar){
51826         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51827     }
51828     // xtype created footer. - not sure if will work as we normally have to render first..
51829     if (this.footer && !this.footer.el && this.footer.xtype) {
51830         
51831         this.footer.container = this.grid.getView().getFooterPanel(true);
51832         this.footer.dataSource = this.grid.dataSource;
51833         this.footer = Roo.factory(this.footer, Roo);
51834         
51835     }
51836     
51837     grid.monitorWindowResize = false; // turn off autosizing
51838     grid.autoHeight = false;
51839     grid.autoWidth = false;
51840     this.grid = grid;
51841     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51842 };
51843
51844 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51845     getId : function(){
51846         return this.grid.id;
51847     },
51848     
51849     /**
51850      * Returns the grid for this panel
51851      * @return {Roo.grid.Grid} 
51852      */
51853     getGrid : function(){
51854         return this.grid;    
51855     },
51856     
51857     setSize : function(width, height){
51858         if(!this.ignoreResize(width, height)){
51859             var grid = this.grid;
51860             var size = this.adjustForComponents(width, height);
51861             grid.getGridEl().setSize(size.width, size.height);
51862             grid.autoSize();
51863         }
51864     },
51865     
51866     beforeSlide : function(){
51867         this.grid.getView().scroller.clip();
51868     },
51869     
51870     afterSlide : function(){
51871         this.grid.getView().scroller.unclip();
51872     },
51873     
51874     destroy : function(){
51875         this.grid.destroy();
51876         delete this.grid;
51877         Roo.GridPanel.superclass.destroy.call(this); 
51878     }
51879 });
51880
51881
51882 /**
51883  * @class Roo.NestedLayoutPanel
51884  * @extends Roo.ContentPanel
51885  * @constructor
51886  * Create a new NestedLayoutPanel.
51887  * 
51888  * 
51889  * @param {Roo.BorderLayout} layout The layout for this panel
51890  * @param {String/Object} config A string to set only the title or a config object
51891  */
51892 Roo.NestedLayoutPanel = function(layout, config)
51893 {
51894     // construct with only one argument..
51895     /* FIXME - implement nicer consturctors
51896     if (layout.layout) {
51897         config = layout;
51898         layout = config.layout;
51899         delete config.layout;
51900     }
51901     if (layout.xtype && !layout.getEl) {
51902         // then layout needs constructing..
51903         layout = Roo.factory(layout, Roo);
51904     }
51905     */
51906     
51907     
51908     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51909     
51910     layout.monitorWindowResize = false; // turn off autosizing
51911     this.layout = layout;
51912     this.layout.getEl().addClass("x-layout-nested-layout");
51913     
51914     
51915     
51916     
51917 };
51918
51919 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51920
51921     setSize : function(width, height){
51922         if(!this.ignoreResize(width, height)){
51923             var size = this.adjustForComponents(width, height);
51924             var el = this.layout.getEl();
51925             el.setSize(size.width, size.height);
51926             var touch = el.dom.offsetWidth;
51927             this.layout.layout();
51928             // ie requires a double layout on the first pass
51929             if(Roo.isIE && !this.initialized){
51930                 this.initialized = true;
51931                 this.layout.layout();
51932             }
51933         }
51934     },
51935     
51936     // activate all subpanels if not currently active..
51937     
51938     setActiveState : function(active){
51939         this.active = active;
51940         if(!active){
51941             this.fireEvent("deactivate", this);
51942             return;
51943         }
51944         
51945         this.fireEvent("activate", this);
51946         // not sure if this should happen before or after..
51947         if (!this.layout) {
51948             return; // should not happen..
51949         }
51950         var reg = false;
51951         for (var r in this.layout.regions) {
51952             reg = this.layout.getRegion(r);
51953             if (reg.getActivePanel()) {
51954                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51955                 reg.setActivePanel(reg.getActivePanel());
51956                 continue;
51957             }
51958             if (!reg.panels.length) {
51959                 continue;
51960             }
51961             reg.showPanel(reg.getPanel(0));
51962         }
51963         
51964         
51965         
51966         
51967     },
51968     
51969     /**
51970      * Returns the nested BorderLayout for this panel
51971      * @return {Roo.BorderLayout} 
51972      */
51973     getLayout : function(){
51974         return this.layout;
51975     },
51976     
51977      /**
51978      * Adds a xtype elements to the layout of the nested panel
51979      * <pre><code>
51980
51981 panel.addxtype({
51982        xtype : 'ContentPanel',
51983        region: 'west',
51984        items: [ .... ]
51985    }
51986 );
51987
51988 panel.addxtype({
51989         xtype : 'NestedLayoutPanel',
51990         region: 'west',
51991         layout: {
51992            center: { },
51993            west: { }   
51994         },
51995         items : [ ... list of content panels or nested layout panels.. ]
51996    }
51997 );
51998 </code></pre>
51999      * @param {Object} cfg Xtype definition of item to add.
52000      */
52001     addxtype : function(cfg) {
52002         return this.layout.addxtype(cfg);
52003     
52004     }
52005 });
52006
52007 Roo.ScrollPanel = function(el, config, content){
52008     config = config || {};
52009     config.fitToFrame = true;
52010     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52011     
52012     this.el.dom.style.overflow = "hidden";
52013     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52014     this.el.removeClass("x-layout-inactive-content");
52015     this.el.on("mousewheel", this.onWheel, this);
52016
52017     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52018     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52019     up.unselectable(); down.unselectable();
52020     up.on("click", this.scrollUp, this);
52021     down.on("click", this.scrollDown, this);
52022     up.addClassOnOver("x-scroller-btn-over");
52023     down.addClassOnOver("x-scroller-btn-over");
52024     up.addClassOnClick("x-scroller-btn-click");
52025     down.addClassOnClick("x-scroller-btn-click");
52026     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52027
52028     this.resizeEl = this.el;
52029     this.el = wrap; this.up = up; this.down = down;
52030 };
52031
52032 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52033     increment : 100,
52034     wheelIncrement : 5,
52035     scrollUp : function(){
52036         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52037     },
52038
52039     scrollDown : function(){
52040         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52041     },
52042
52043     afterScroll : function(){
52044         var el = this.resizeEl;
52045         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52046         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52047         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52048     },
52049
52050     setSize : function(){
52051         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52052         this.afterScroll();
52053     },
52054
52055     onWheel : function(e){
52056         var d = e.getWheelDelta();
52057         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52058         this.afterScroll();
52059         e.stopEvent();
52060     },
52061
52062     setContent : function(content, loadScripts){
52063         this.resizeEl.update(content, loadScripts);
52064     }
52065
52066 });
52067
52068
52069
52070
52071
52072
52073
52074
52075
52076 /**
52077  * @class Roo.TreePanel
52078  * @extends Roo.ContentPanel
52079  * @constructor
52080  * Create a new TreePanel. - defaults to fit/scoll contents.
52081  * @param {String/Object} config A string to set only the panel's title, or a config object
52082  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52083  */
52084 Roo.TreePanel = function(config){
52085     var el = config.el;
52086     var tree = config.tree;
52087     delete config.tree; 
52088     delete config.el; // hopefull!
52089     
52090     // wrapper for IE7 strict & safari scroll issue
52091     
52092     var treeEl = el.createChild();
52093     config.resizeEl = treeEl;
52094     
52095     
52096     
52097     Roo.TreePanel.superclass.constructor.call(this, el, config);
52098  
52099  
52100     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52101     //console.log(tree);
52102     this.on('activate', function()
52103     {
52104         if (this.tree.rendered) {
52105             return;
52106         }
52107         //console.log('render tree');
52108         this.tree.render();
52109     });
52110     // this should not be needed.. - it's actually the 'el' that resizes?
52111     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52112     
52113     //this.on('resize',  function (cp, w, h) {
52114     //        this.tree.innerCt.setWidth(w);
52115     //        this.tree.innerCt.setHeight(h);
52116     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52117     //});
52118
52119         
52120     
52121 };
52122
52123 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52124     fitToFrame : true,
52125     autoScroll : true
52126 });
52127
52128
52129
52130
52131
52132
52133
52134
52135
52136
52137
52138 /*
52139  * Based on:
52140  * Ext JS Library 1.1.1
52141  * Copyright(c) 2006-2007, Ext JS, LLC.
52142  *
52143  * Originally Released Under LGPL - original licence link has changed is not relivant.
52144  *
52145  * Fork - LGPL
52146  * <script type="text/javascript">
52147  */
52148  
52149
52150 /**
52151  * @class Roo.ReaderLayout
52152  * @extends Roo.BorderLayout
52153  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52154  * center region containing two nested regions (a top one for a list view and one for item preview below),
52155  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52156  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52157  * expedites the setup of the overall layout and regions for this common application style.
52158  * Example:
52159  <pre><code>
52160 var reader = new Roo.ReaderLayout();
52161 var CP = Roo.ContentPanel;  // shortcut for adding
52162
52163 reader.beginUpdate();
52164 reader.add("north", new CP("north", "North"));
52165 reader.add("west", new CP("west", {title: "West"}));
52166 reader.add("east", new CP("east", {title: "East"}));
52167
52168 reader.regions.listView.add(new CP("listView", "List"));
52169 reader.regions.preview.add(new CP("preview", "Preview"));
52170 reader.endUpdate();
52171 </code></pre>
52172 * @constructor
52173 * Create a new ReaderLayout
52174 * @param {Object} config Configuration options
52175 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52176 * document.body if omitted)
52177 */
52178 Roo.ReaderLayout = function(config, renderTo){
52179     var c = config || {size:{}};
52180     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52181         north: c.north !== false ? Roo.apply({
52182             split:false,
52183             initialSize: 32,
52184             titlebar: false
52185         }, c.north) : false,
52186         west: c.west !== false ? Roo.apply({
52187             split:true,
52188             initialSize: 200,
52189             minSize: 175,
52190             maxSize: 400,
52191             titlebar: true,
52192             collapsible: true,
52193             animate: true,
52194             margins:{left:5,right:0,bottom:5,top:5},
52195             cmargins:{left:5,right:5,bottom:5,top:5}
52196         }, c.west) : false,
52197         east: c.east !== false ? Roo.apply({
52198             split:true,
52199             initialSize: 200,
52200             minSize: 175,
52201             maxSize: 400,
52202             titlebar: true,
52203             collapsible: true,
52204             animate: true,
52205             margins:{left:0,right:5,bottom:5,top:5},
52206             cmargins:{left:5,right:5,bottom:5,top:5}
52207         }, c.east) : false,
52208         center: Roo.apply({
52209             tabPosition: 'top',
52210             autoScroll:false,
52211             closeOnTab: true,
52212             titlebar:false,
52213             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52214         }, c.center)
52215     });
52216
52217     this.el.addClass('x-reader');
52218
52219     this.beginUpdate();
52220
52221     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52222         south: c.preview !== false ? Roo.apply({
52223             split:true,
52224             initialSize: 200,
52225             minSize: 100,
52226             autoScroll:true,
52227             collapsible:true,
52228             titlebar: true,
52229             cmargins:{top:5,left:0, right:0, bottom:0}
52230         }, c.preview) : false,
52231         center: Roo.apply({
52232             autoScroll:false,
52233             titlebar:false,
52234             minHeight:200
52235         }, c.listView)
52236     });
52237     this.add('center', new Roo.NestedLayoutPanel(inner,
52238             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52239
52240     this.endUpdate();
52241
52242     this.regions.preview = inner.getRegion('south');
52243     this.regions.listView = inner.getRegion('center');
52244 };
52245
52246 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52247  * Based on:
52248  * Ext JS Library 1.1.1
52249  * Copyright(c) 2006-2007, Ext JS, LLC.
52250  *
52251  * Originally Released Under LGPL - original licence link has changed is not relivant.
52252  *
52253  * Fork - LGPL
52254  * <script type="text/javascript">
52255  */
52256  
52257 /**
52258  * @class Roo.grid.Grid
52259  * @extends Roo.util.Observable
52260  * This class represents the primary interface of a component based grid control.
52261  * <br><br>Usage:<pre><code>
52262  var grid = new Roo.grid.Grid("my-container-id", {
52263      ds: myDataStore,
52264      cm: myColModel,
52265      selModel: mySelectionModel,
52266      autoSizeColumns: true,
52267      monitorWindowResize: false,
52268      trackMouseOver: true
52269  });
52270  // set any options
52271  grid.render();
52272  * </code></pre>
52273  * <b>Common Problems:</b><br/>
52274  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52275  * element will correct this<br/>
52276  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52277  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52278  * are unpredictable.<br/>
52279  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52280  * grid to calculate dimensions/offsets.<br/>
52281   * @constructor
52282  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52283  * The container MUST have some type of size defined for the grid to fill. The container will be
52284  * automatically set to position relative if it isn't already.
52285  * @param {Object} config A config object that sets properties on this grid.
52286  */
52287 Roo.grid.Grid = function(container, config){
52288         // initialize the container
52289         this.container = Roo.get(container);
52290         this.container.update("");
52291         this.container.setStyle("overflow", "hidden");
52292     this.container.addClass('x-grid-container');
52293
52294     this.id = this.container.id;
52295
52296     Roo.apply(this, config);
52297     // check and correct shorthanded configs
52298     if(this.ds){
52299         this.dataSource = this.ds;
52300         delete this.ds;
52301     }
52302     if(this.cm){
52303         this.colModel = this.cm;
52304         delete this.cm;
52305     }
52306     if(this.sm){
52307         this.selModel = this.sm;
52308         delete this.sm;
52309     }
52310
52311     if (this.selModel) {
52312         this.selModel = Roo.factory(this.selModel, Roo.grid);
52313         this.sm = this.selModel;
52314         this.sm.xmodule = this.xmodule || false;
52315     }
52316     if (typeof(this.colModel.config) == 'undefined') {
52317         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52318         this.cm = this.colModel;
52319         this.cm.xmodule = this.xmodule || false;
52320     }
52321     if (this.dataSource) {
52322         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52323         this.ds = this.dataSource;
52324         this.ds.xmodule = this.xmodule || false;
52325          
52326     }
52327     
52328     
52329     
52330     if(this.width){
52331         this.container.setWidth(this.width);
52332     }
52333
52334     if(this.height){
52335         this.container.setHeight(this.height);
52336     }
52337     /** @private */
52338         this.addEvents({
52339         // raw events
52340         /**
52341          * @event click
52342          * The raw click event for the entire grid.
52343          * @param {Roo.EventObject} e
52344          */
52345         "click" : true,
52346         /**
52347          * @event dblclick
52348          * The raw dblclick event for the entire grid.
52349          * @param {Roo.EventObject} e
52350          */
52351         "dblclick" : true,
52352         /**
52353          * @event contextmenu
52354          * The raw contextmenu event for the entire grid.
52355          * @param {Roo.EventObject} e
52356          */
52357         "contextmenu" : true,
52358         /**
52359          * @event mousedown
52360          * The raw mousedown event for the entire grid.
52361          * @param {Roo.EventObject} e
52362          */
52363         "mousedown" : true,
52364         /**
52365          * @event mouseup
52366          * The raw mouseup event for the entire grid.
52367          * @param {Roo.EventObject} e
52368          */
52369         "mouseup" : true,
52370         /**
52371          * @event mouseover
52372          * The raw mouseover event for the entire grid.
52373          * @param {Roo.EventObject} e
52374          */
52375         "mouseover" : true,
52376         /**
52377          * @event mouseout
52378          * The raw mouseout event for the entire grid.
52379          * @param {Roo.EventObject} e
52380          */
52381         "mouseout" : true,
52382         /**
52383          * @event keypress
52384          * The raw keypress event for the entire grid.
52385          * @param {Roo.EventObject} e
52386          */
52387         "keypress" : true,
52388         /**
52389          * @event keydown
52390          * The raw keydown event for the entire grid.
52391          * @param {Roo.EventObject} e
52392          */
52393         "keydown" : true,
52394
52395         // custom events
52396
52397         /**
52398          * @event cellclick
52399          * Fires when a cell is clicked
52400          * @param {Grid} this
52401          * @param {Number} rowIndex
52402          * @param {Number} columnIndex
52403          * @param {Roo.EventObject} e
52404          */
52405         "cellclick" : true,
52406         /**
52407          * @event celldblclick
52408          * Fires when a cell is double clicked
52409          * @param {Grid} this
52410          * @param {Number} rowIndex
52411          * @param {Number} columnIndex
52412          * @param {Roo.EventObject} e
52413          */
52414         "celldblclick" : true,
52415         /**
52416          * @event rowclick
52417          * Fires when a row is clicked
52418          * @param {Grid} this
52419          * @param {Number} rowIndex
52420          * @param {Roo.EventObject} e
52421          */
52422         "rowclick" : true,
52423         /**
52424          * @event rowdblclick
52425          * Fires when a row is double clicked
52426          * @param {Grid} this
52427          * @param {Number} rowIndex
52428          * @param {Roo.EventObject} e
52429          */
52430         "rowdblclick" : true,
52431         /**
52432          * @event headerclick
52433          * Fires when a header is clicked
52434          * @param {Grid} this
52435          * @param {Number} columnIndex
52436          * @param {Roo.EventObject} e
52437          */
52438         "headerclick" : true,
52439         /**
52440          * @event headerdblclick
52441          * Fires when a header cell is double clicked
52442          * @param {Grid} this
52443          * @param {Number} columnIndex
52444          * @param {Roo.EventObject} e
52445          */
52446         "headerdblclick" : true,
52447         /**
52448          * @event rowcontextmenu
52449          * Fires when a row is right clicked
52450          * @param {Grid} this
52451          * @param {Number} rowIndex
52452          * @param {Roo.EventObject} e
52453          */
52454         "rowcontextmenu" : true,
52455         /**
52456          * @event cellcontextmenu
52457          * Fires when a cell is right clicked
52458          * @param {Grid} this
52459          * @param {Number} rowIndex
52460          * @param {Number} cellIndex
52461          * @param {Roo.EventObject} e
52462          */
52463          "cellcontextmenu" : true,
52464         /**
52465          * @event headercontextmenu
52466          * Fires when a header is right clicked
52467          * @param {Grid} this
52468          * @param {Number} columnIndex
52469          * @param {Roo.EventObject} e
52470          */
52471         "headercontextmenu" : true,
52472         /**
52473          * @event bodyscroll
52474          * Fires when the body element is scrolled
52475          * @param {Number} scrollLeft
52476          * @param {Number} scrollTop
52477          */
52478         "bodyscroll" : true,
52479         /**
52480          * @event columnresize
52481          * Fires when the user resizes a column
52482          * @param {Number} columnIndex
52483          * @param {Number} newSize
52484          */
52485         "columnresize" : true,
52486         /**
52487          * @event columnmove
52488          * Fires when the user moves a column
52489          * @param {Number} oldIndex
52490          * @param {Number} newIndex
52491          */
52492         "columnmove" : true,
52493         /**
52494          * @event startdrag
52495          * Fires when row(s) start being dragged
52496          * @param {Grid} this
52497          * @param {Roo.GridDD} dd The drag drop object
52498          * @param {event} e The raw browser event
52499          */
52500         "startdrag" : true,
52501         /**
52502          * @event enddrag
52503          * Fires when a drag operation is complete
52504          * @param {Grid} this
52505          * @param {Roo.GridDD} dd The drag drop object
52506          * @param {event} e The raw browser event
52507          */
52508         "enddrag" : true,
52509         /**
52510          * @event dragdrop
52511          * Fires when dragged row(s) are dropped on a valid DD target
52512          * @param {Grid} this
52513          * @param {Roo.GridDD} dd The drag drop object
52514          * @param {String} targetId The target drag drop object
52515          * @param {event} e The raw browser event
52516          */
52517         "dragdrop" : true,
52518         /**
52519          * @event dragover
52520          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52521          * @param {Grid} this
52522          * @param {Roo.GridDD} dd The drag drop object
52523          * @param {String} targetId The target drag drop object
52524          * @param {event} e The raw browser event
52525          */
52526         "dragover" : true,
52527         /**
52528          * @event dragenter
52529          *  Fires when the dragged row(s) first cross another DD target while being dragged
52530          * @param {Grid} this
52531          * @param {Roo.GridDD} dd The drag drop object
52532          * @param {String} targetId The target drag drop object
52533          * @param {event} e The raw browser event
52534          */
52535         "dragenter" : true,
52536         /**
52537          * @event dragout
52538          * Fires when the dragged row(s) leave another DD target while being dragged
52539          * @param {Grid} this
52540          * @param {Roo.GridDD} dd The drag drop object
52541          * @param {String} targetId The target drag drop object
52542          * @param {event} e The raw browser event
52543          */
52544         "dragout" : true,
52545         /**
52546          * @event rowclass
52547          * Fires when a row is rendered, so you can change add a style to it.
52548          * @param {GridView} gridview   The grid view
52549          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52550          */
52551         'rowclass' : true,
52552
52553         /**
52554          * @event render
52555          * Fires when the grid is rendered
52556          * @param {Grid} grid
52557          */
52558         'render' : true
52559     });
52560
52561     Roo.grid.Grid.superclass.constructor.call(this);
52562 };
52563 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52564     
52565     /**
52566      * @cfg {String} ddGroup - drag drop group.
52567      */
52568
52569     /**
52570      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52571      */
52572     minColumnWidth : 25,
52573
52574     /**
52575      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52576      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52577      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52578      */
52579     autoSizeColumns : false,
52580
52581     /**
52582      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52583      */
52584     autoSizeHeaders : true,
52585
52586     /**
52587      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52588      */
52589     monitorWindowResize : true,
52590
52591     /**
52592      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52593      * rows measured to get a columns size. Default is 0 (all rows).
52594      */
52595     maxRowsToMeasure : 0,
52596
52597     /**
52598      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52599      */
52600     trackMouseOver : true,
52601
52602     /**
52603     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52604     */
52605     
52606     /**
52607     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52608     */
52609     enableDragDrop : false,
52610     
52611     /**
52612     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52613     */
52614     enableColumnMove : true,
52615     
52616     /**
52617     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52618     */
52619     enableColumnHide : true,
52620     
52621     /**
52622     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52623     */
52624     enableRowHeightSync : false,
52625     
52626     /**
52627     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52628     */
52629     stripeRows : true,
52630     
52631     /**
52632     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52633     */
52634     autoHeight : false,
52635
52636     /**
52637      * @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.
52638      */
52639     autoExpandColumn : false,
52640
52641     /**
52642     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52643     * Default is 50.
52644     */
52645     autoExpandMin : 50,
52646
52647     /**
52648     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52649     */
52650     autoExpandMax : 1000,
52651
52652     /**
52653     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52654     */
52655     view : null,
52656
52657     /**
52658     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52659     */
52660     loadMask : false,
52661     /**
52662     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52663     */
52664     dropTarget: false,
52665     
52666    
52667     
52668     // private
52669     rendered : false,
52670
52671     /**
52672     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52673     * of a fixed width. Default is false.
52674     */
52675     /**
52676     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52677     */
52678     /**
52679      * Called once after all setup has been completed and the grid is ready to be rendered.
52680      * @return {Roo.grid.Grid} this
52681      */
52682     render : function()
52683     {
52684         var c = this.container;
52685         // try to detect autoHeight/width mode
52686         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52687             this.autoHeight = true;
52688         }
52689         var view = this.getView();
52690         view.init(this);
52691
52692         c.on("click", this.onClick, this);
52693         c.on("dblclick", this.onDblClick, this);
52694         c.on("contextmenu", this.onContextMenu, this);
52695         c.on("keydown", this.onKeyDown, this);
52696         if (Roo.isTouch) {
52697             c.on("touchstart", this.onTouchStart, this);
52698         }
52699
52700         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52701
52702         this.getSelectionModel().init(this);
52703
52704         view.render();
52705
52706         if(this.loadMask){
52707             this.loadMask = new Roo.LoadMask(this.container,
52708                     Roo.apply({store:this.dataSource}, this.loadMask));
52709         }
52710         
52711         
52712         if (this.toolbar && this.toolbar.xtype) {
52713             this.toolbar.container = this.getView().getHeaderPanel(true);
52714             this.toolbar = new Roo.Toolbar(this.toolbar);
52715         }
52716         if (this.footer && this.footer.xtype) {
52717             this.footer.dataSource = this.getDataSource();
52718             this.footer.container = this.getView().getFooterPanel(true);
52719             this.footer = Roo.factory(this.footer, Roo);
52720         }
52721         if (this.dropTarget && this.dropTarget.xtype) {
52722             delete this.dropTarget.xtype;
52723             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52724         }
52725         
52726         
52727         this.rendered = true;
52728         this.fireEvent('render', this);
52729         return this;
52730     },
52731
52732         /**
52733          * Reconfigures the grid to use a different Store and Column Model.
52734          * The View will be bound to the new objects and refreshed.
52735          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52736          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52737          */
52738     reconfigure : function(dataSource, colModel){
52739         if(this.loadMask){
52740             this.loadMask.destroy();
52741             this.loadMask = new Roo.LoadMask(this.container,
52742                     Roo.apply({store:dataSource}, this.loadMask));
52743         }
52744         this.view.bind(dataSource, colModel);
52745         this.dataSource = dataSource;
52746         this.colModel = colModel;
52747         this.view.refresh(true);
52748     },
52749
52750     // private
52751     onKeyDown : function(e){
52752         this.fireEvent("keydown", e);
52753     },
52754
52755     /**
52756      * Destroy this grid.
52757      * @param {Boolean} removeEl True to remove the element
52758      */
52759     destroy : function(removeEl, keepListeners){
52760         if(this.loadMask){
52761             this.loadMask.destroy();
52762         }
52763         var c = this.container;
52764         c.removeAllListeners();
52765         this.view.destroy();
52766         this.colModel.purgeListeners();
52767         if(!keepListeners){
52768             this.purgeListeners();
52769         }
52770         c.update("");
52771         if(removeEl === true){
52772             c.remove();
52773         }
52774     },
52775
52776     // private
52777     processEvent : function(name, e){
52778         // does this fire select???
52779         //Roo.log('grid:processEvent '  + name);
52780         
52781         if (name != 'touchstart' ) {
52782             this.fireEvent(name, e);    
52783         }
52784         
52785         var t = e.getTarget();
52786         var v = this.view;
52787         var header = v.findHeaderIndex(t);
52788         if(header !== false){
52789             var ename = name == 'touchstart' ? 'click' : name;
52790              
52791             this.fireEvent("header" + ename, this, header, e);
52792         }else{
52793             var row = v.findRowIndex(t);
52794             var cell = v.findCellIndex(t);
52795             if (name == 'touchstart') {
52796                 // first touch is always a click.
52797                 // hopefull this happens after selection is updated.?
52798                 name = false;
52799                 
52800                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52801                     var cs = this.selModel.getSelectedCell();
52802                     if (row == cs[0] && cell == cs[1]){
52803                         name = 'dblclick';
52804                     }
52805                 }
52806                 if (typeof(this.selModel.getSelections) != 'undefined') {
52807                     var cs = this.selModel.getSelections();
52808                     var ds = this.dataSource;
52809                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52810                         name = 'dblclick';
52811                     }
52812                 }
52813                 if (!name) {
52814                     return;
52815                 }
52816             }
52817             
52818             
52819             if(row !== false){
52820                 this.fireEvent("row" + name, this, row, e);
52821                 if(cell !== false){
52822                     this.fireEvent("cell" + name, this, row, cell, e);
52823                 }
52824             }
52825         }
52826     },
52827
52828     // private
52829     onClick : function(e){
52830         this.processEvent("click", e);
52831     },
52832    // private
52833     onTouchStart : function(e){
52834         this.processEvent("touchstart", e);
52835     },
52836
52837     // private
52838     onContextMenu : function(e, t){
52839         this.processEvent("contextmenu", e);
52840     },
52841
52842     // private
52843     onDblClick : function(e){
52844         this.processEvent("dblclick", e);
52845     },
52846
52847     // private
52848     walkCells : function(row, col, step, fn, scope){
52849         var cm = this.colModel, clen = cm.getColumnCount();
52850         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52851         if(step < 0){
52852             if(col < 0){
52853                 row--;
52854                 first = false;
52855             }
52856             while(row >= 0){
52857                 if(!first){
52858                     col = clen-1;
52859                 }
52860                 first = false;
52861                 while(col >= 0){
52862                     if(fn.call(scope || this, row, col, cm) === true){
52863                         return [row, col];
52864                     }
52865                     col--;
52866                 }
52867                 row--;
52868             }
52869         } else {
52870             if(col >= clen){
52871                 row++;
52872                 first = false;
52873             }
52874             while(row < rlen){
52875                 if(!first){
52876                     col = 0;
52877                 }
52878                 first = false;
52879                 while(col < clen){
52880                     if(fn.call(scope || this, row, col, cm) === true){
52881                         return [row, col];
52882                     }
52883                     col++;
52884                 }
52885                 row++;
52886             }
52887         }
52888         return null;
52889     },
52890
52891     // private
52892     getSelections : function(){
52893         return this.selModel.getSelections();
52894     },
52895
52896     /**
52897      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52898      * but if manual update is required this method will initiate it.
52899      */
52900     autoSize : function(){
52901         if(this.rendered){
52902             this.view.layout();
52903             if(this.view.adjustForScroll){
52904                 this.view.adjustForScroll();
52905             }
52906         }
52907     },
52908
52909     /**
52910      * Returns the grid's underlying element.
52911      * @return {Element} The element
52912      */
52913     getGridEl : function(){
52914         return this.container;
52915     },
52916
52917     // private for compatibility, overridden by editor grid
52918     stopEditing : function(){},
52919
52920     /**
52921      * Returns the grid's SelectionModel.
52922      * @return {SelectionModel}
52923      */
52924     getSelectionModel : function(){
52925         if(!this.selModel){
52926             this.selModel = new Roo.grid.RowSelectionModel();
52927         }
52928         return this.selModel;
52929     },
52930
52931     /**
52932      * Returns the grid's DataSource.
52933      * @return {DataSource}
52934      */
52935     getDataSource : function(){
52936         return this.dataSource;
52937     },
52938
52939     /**
52940      * Returns the grid's ColumnModel.
52941      * @return {ColumnModel}
52942      */
52943     getColumnModel : function(){
52944         return this.colModel;
52945     },
52946
52947     /**
52948      * Returns the grid's GridView object.
52949      * @return {GridView}
52950      */
52951     getView : function(){
52952         if(!this.view){
52953             this.view = new Roo.grid.GridView(this.viewConfig);
52954         }
52955         return this.view;
52956     },
52957     /**
52958      * Called to get grid's drag proxy text, by default returns this.ddText.
52959      * @return {String}
52960      */
52961     getDragDropText : function(){
52962         var count = this.selModel.getCount();
52963         return String.format(this.ddText, count, count == 1 ? '' : 's');
52964     }
52965 });
52966 /**
52967  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52968  * %0 is replaced with the number of selected rows.
52969  * @type String
52970  */
52971 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52972  * Based on:
52973  * Ext JS Library 1.1.1
52974  * Copyright(c) 2006-2007, Ext JS, LLC.
52975  *
52976  * Originally Released Under LGPL - original licence link has changed is not relivant.
52977  *
52978  * Fork - LGPL
52979  * <script type="text/javascript">
52980  */
52981  
52982 Roo.grid.AbstractGridView = function(){
52983         this.grid = null;
52984         
52985         this.events = {
52986             "beforerowremoved" : true,
52987             "beforerowsinserted" : true,
52988             "beforerefresh" : true,
52989             "rowremoved" : true,
52990             "rowsinserted" : true,
52991             "rowupdated" : true,
52992             "refresh" : true
52993         };
52994     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52995 };
52996
52997 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52998     rowClass : "x-grid-row",
52999     cellClass : "x-grid-cell",
53000     tdClass : "x-grid-td",
53001     hdClass : "x-grid-hd",
53002     splitClass : "x-grid-hd-split",
53003     
53004     init: function(grid){
53005         this.grid = grid;
53006                 var cid = this.grid.getGridEl().id;
53007         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53008         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53009         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53010         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53011         },
53012         
53013     getColumnRenderers : function(){
53014         var renderers = [];
53015         var cm = this.grid.colModel;
53016         var colCount = cm.getColumnCount();
53017         for(var i = 0; i < colCount; i++){
53018             renderers[i] = cm.getRenderer(i);
53019         }
53020         return renderers;
53021     },
53022     
53023     getColumnIds : function(){
53024         var ids = [];
53025         var cm = this.grid.colModel;
53026         var colCount = cm.getColumnCount();
53027         for(var i = 0; i < colCount; i++){
53028             ids[i] = cm.getColumnId(i);
53029         }
53030         return ids;
53031     },
53032     
53033     getDataIndexes : function(){
53034         if(!this.indexMap){
53035             this.indexMap = this.buildIndexMap();
53036         }
53037         return this.indexMap.colToData;
53038     },
53039     
53040     getColumnIndexByDataIndex : function(dataIndex){
53041         if(!this.indexMap){
53042             this.indexMap = this.buildIndexMap();
53043         }
53044         return this.indexMap.dataToCol[dataIndex];
53045     },
53046     
53047     /**
53048      * Set a css style for a column dynamically. 
53049      * @param {Number} colIndex The index of the column
53050      * @param {String} name The css property name
53051      * @param {String} value The css value
53052      */
53053     setCSSStyle : function(colIndex, name, value){
53054         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53055         Roo.util.CSS.updateRule(selector, name, value);
53056     },
53057     
53058     generateRules : function(cm){
53059         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53060         Roo.util.CSS.removeStyleSheet(rulesId);
53061         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53062             var cid = cm.getColumnId(i);
53063             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53064                          this.tdSelector, cid, " {\n}\n",
53065                          this.hdSelector, cid, " {\n}\n",
53066                          this.splitSelector, cid, " {\n}\n");
53067         }
53068         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53069     }
53070 });/*
53071  * Based on:
53072  * Ext JS Library 1.1.1
53073  * Copyright(c) 2006-2007, Ext JS, LLC.
53074  *
53075  * Originally Released Under LGPL - original licence link has changed is not relivant.
53076  *
53077  * Fork - LGPL
53078  * <script type="text/javascript">
53079  */
53080
53081 // private
53082 // This is a support class used internally by the Grid components
53083 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53084     this.grid = grid;
53085     this.view = grid.getView();
53086     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53087     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53088     if(hd2){
53089         this.setHandleElId(Roo.id(hd));
53090         this.setOuterHandleElId(Roo.id(hd2));
53091     }
53092     this.scroll = false;
53093 };
53094 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53095     maxDragWidth: 120,
53096     getDragData : function(e){
53097         var t = Roo.lib.Event.getTarget(e);
53098         var h = this.view.findHeaderCell(t);
53099         if(h){
53100             return {ddel: h.firstChild, header:h};
53101         }
53102         return false;
53103     },
53104
53105     onInitDrag : function(e){
53106         this.view.headersDisabled = true;
53107         var clone = this.dragData.ddel.cloneNode(true);
53108         clone.id = Roo.id();
53109         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53110         this.proxy.update(clone);
53111         return true;
53112     },
53113
53114     afterValidDrop : function(){
53115         var v = this.view;
53116         setTimeout(function(){
53117             v.headersDisabled = false;
53118         }, 50);
53119     },
53120
53121     afterInvalidDrop : function(){
53122         var v = this.view;
53123         setTimeout(function(){
53124             v.headersDisabled = false;
53125         }, 50);
53126     }
53127 });
53128 /*
53129  * Based on:
53130  * Ext JS Library 1.1.1
53131  * Copyright(c) 2006-2007, Ext JS, LLC.
53132  *
53133  * Originally Released Under LGPL - original licence link has changed is not relivant.
53134  *
53135  * Fork - LGPL
53136  * <script type="text/javascript">
53137  */
53138 // private
53139 // This is a support class used internally by the Grid components
53140 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53141     this.grid = grid;
53142     this.view = grid.getView();
53143     // split the proxies so they don't interfere with mouse events
53144     this.proxyTop = Roo.DomHelper.append(document.body, {
53145         cls:"col-move-top", html:"&#160;"
53146     }, true);
53147     this.proxyBottom = Roo.DomHelper.append(document.body, {
53148         cls:"col-move-bottom", html:"&#160;"
53149     }, true);
53150     this.proxyTop.hide = this.proxyBottom.hide = function(){
53151         this.setLeftTop(-100,-100);
53152         this.setStyle("visibility", "hidden");
53153     };
53154     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53155     // temporarily disabled
53156     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53157     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53158 };
53159 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53160     proxyOffsets : [-4, -9],
53161     fly: Roo.Element.fly,
53162
53163     getTargetFromEvent : function(e){
53164         var t = Roo.lib.Event.getTarget(e);
53165         var cindex = this.view.findCellIndex(t);
53166         if(cindex !== false){
53167             return this.view.getHeaderCell(cindex);
53168         }
53169         return null;
53170     },
53171
53172     nextVisible : function(h){
53173         var v = this.view, cm = this.grid.colModel;
53174         h = h.nextSibling;
53175         while(h){
53176             if(!cm.isHidden(v.getCellIndex(h))){
53177                 return h;
53178             }
53179             h = h.nextSibling;
53180         }
53181         return null;
53182     },
53183
53184     prevVisible : function(h){
53185         var v = this.view, cm = this.grid.colModel;
53186         h = h.prevSibling;
53187         while(h){
53188             if(!cm.isHidden(v.getCellIndex(h))){
53189                 return h;
53190             }
53191             h = h.prevSibling;
53192         }
53193         return null;
53194     },
53195
53196     positionIndicator : function(h, n, e){
53197         var x = Roo.lib.Event.getPageX(e);
53198         var r = Roo.lib.Dom.getRegion(n.firstChild);
53199         var px, pt, py = r.top + this.proxyOffsets[1];
53200         if((r.right - x) <= (r.right-r.left)/2){
53201             px = r.right+this.view.borderWidth;
53202             pt = "after";
53203         }else{
53204             px = r.left;
53205             pt = "before";
53206         }
53207         var oldIndex = this.view.getCellIndex(h);
53208         var newIndex = this.view.getCellIndex(n);
53209
53210         if(this.grid.colModel.isFixed(newIndex)){
53211             return false;
53212         }
53213
53214         var locked = this.grid.colModel.isLocked(newIndex);
53215
53216         if(pt == "after"){
53217             newIndex++;
53218         }
53219         if(oldIndex < newIndex){
53220             newIndex--;
53221         }
53222         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53223             return false;
53224         }
53225         px +=  this.proxyOffsets[0];
53226         this.proxyTop.setLeftTop(px, py);
53227         this.proxyTop.show();
53228         if(!this.bottomOffset){
53229             this.bottomOffset = this.view.mainHd.getHeight();
53230         }
53231         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53232         this.proxyBottom.show();
53233         return pt;
53234     },
53235
53236     onNodeEnter : function(n, dd, e, data){
53237         if(data.header != n){
53238             this.positionIndicator(data.header, n, e);
53239         }
53240     },
53241
53242     onNodeOver : function(n, dd, e, data){
53243         var result = false;
53244         if(data.header != n){
53245             result = this.positionIndicator(data.header, n, e);
53246         }
53247         if(!result){
53248             this.proxyTop.hide();
53249             this.proxyBottom.hide();
53250         }
53251         return result ? this.dropAllowed : this.dropNotAllowed;
53252     },
53253
53254     onNodeOut : function(n, dd, e, data){
53255         this.proxyTop.hide();
53256         this.proxyBottom.hide();
53257     },
53258
53259     onNodeDrop : function(n, dd, e, data){
53260         var h = data.header;
53261         if(h != n){
53262             var cm = this.grid.colModel;
53263             var x = Roo.lib.Event.getPageX(e);
53264             var r = Roo.lib.Dom.getRegion(n.firstChild);
53265             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53266             var oldIndex = this.view.getCellIndex(h);
53267             var newIndex = this.view.getCellIndex(n);
53268             var locked = cm.isLocked(newIndex);
53269             if(pt == "after"){
53270                 newIndex++;
53271             }
53272             if(oldIndex < newIndex){
53273                 newIndex--;
53274             }
53275             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53276                 return false;
53277             }
53278             cm.setLocked(oldIndex, locked, true);
53279             cm.moveColumn(oldIndex, newIndex);
53280             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53281             return true;
53282         }
53283         return false;
53284     }
53285 });
53286 /*
53287  * Based on:
53288  * Ext JS Library 1.1.1
53289  * Copyright(c) 2006-2007, Ext JS, LLC.
53290  *
53291  * Originally Released Under LGPL - original licence link has changed is not relivant.
53292  *
53293  * Fork - LGPL
53294  * <script type="text/javascript">
53295  */
53296   
53297 /**
53298  * @class Roo.grid.GridView
53299  * @extends Roo.util.Observable
53300  *
53301  * @constructor
53302  * @param {Object} config
53303  */
53304 Roo.grid.GridView = function(config){
53305     Roo.grid.GridView.superclass.constructor.call(this);
53306     this.el = null;
53307
53308     Roo.apply(this, config);
53309 };
53310
53311 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53312
53313     unselectable :  'unselectable="on"',
53314     unselectableCls :  'x-unselectable',
53315     
53316     
53317     rowClass : "x-grid-row",
53318
53319     cellClass : "x-grid-col",
53320
53321     tdClass : "x-grid-td",
53322
53323     hdClass : "x-grid-hd",
53324
53325     splitClass : "x-grid-split",
53326
53327     sortClasses : ["sort-asc", "sort-desc"],
53328
53329     enableMoveAnim : false,
53330
53331     hlColor: "C3DAF9",
53332
53333     dh : Roo.DomHelper,
53334
53335     fly : Roo.Element.fly,
53336
53337     css : Roo.util.CSS,
53338
53339     borderWidth: 1,
53340
53341     splitOffset: 3,
53342
53343     scrollIncrement : 22,
53344
53345     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53346
53347     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53348
53349     bind : function(ds, cm){
53350         if(this.ds){
53351             this.ds.un("load", this.onLoad, this);
53352             this.ds.un("datachanged", this.onDataChange, this);
53353             this.ds.un("add", this.onAdd, this);
53354             this.ds.un("remove", this.onRemove, this);
53355             this.ds.un("update", this.onUpdate, this);
53356             this.ds.un("clear", this.onClear, this);
53357         }
53358         if(ds){
53359             ds.on("load", this.onLoad, this);
53360             ds.on("datachanged", this.onDataChange, this);
53361             ds.on("add", this.onAdd, this);
53362             ds.on("remove", this.onRemove, this);
53363             ds.on("update", this.onUpdate, this);
53364             ds.on("clear", this.onClear, this);
53365         }
53366         this.ds = ds;
53367
53368         if(this.cm){
53369             this.cm.un("widthchange", this.onColWidthChange, this);
53370             this.cm.un("headerchange", this.onHeaderChange, this);
53371             this.cm.un("hiddenchange", this.onHiddenChange, this);
53372             this.cm.un("columnmoved", this.onColumnMove, this);
53373             this.cm.un("columnlockchange", this.onColumnLock, this);
53374         }
53375         if(cm){
53376             this.generateRules(cm);
53377             cm.on("widthchange", this.onColWidthChange, this);
53378             cm.on("headerchange", this.onHeaderChange, this);
53379             cm.on("hiddenchange", this.onHiddenChange, this);
53380             cm.on("columnmoved", this.onColumnMove, this);
53381             cm.on("columnlockchange", this.onColumnLock, this);
53382         }
53383         this.cm = cm;
53384     },
53385
53386     init: function(grid){
53387         Roo.grid.GridView.superclass.init.call(this, grid);
53388
53389         this.bind(grid.dataSource, grid.colModel);
53390
53391         grid.on("headerclick", this.handleHeaderClick, this);
53392
53393         if(grid.trackMouseOver){
53394             grid.on("mouseover", this.onRowOver, this);
53395             grid.on("mouseout", this.onRowOut, this);
53396         }
53397         grid.cancelTextSelection = function(){};
53398         this.gridId = grid.id;
53399
53400         var tpls = this.templates || {};
53401
53402         if(!tpls.master){
53403             tpls.master = new Roo.Template(
53404                '<div class="x-grid" hidefocus="true">',
53405                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53406                   '<div class="x-grid-topbar"></div>',
53407                   '<div class="x-grid-scroller"><div></div></div>',
53408                   '<div class="x-grid-locked">',
53409                       '<div class="x-grid-header">{lockedHeader}</div>',
53410                       '<div class="x-grid-body">{lockedBody}</div>',
53411                   "</div>",
53412                   '<div class="x-grid-viewport">',
53413                       '<div class="x-grid-header">{header}</div>',
53414                       '<div class="x-grid-body">{body}</div>',
53415                   "</div>",
53416                   '<div class="x-grid-bottombar"></div>',
53417                  
53418                   '<div class="x-grid-resize-proxy">&#160;</div>',
53419                "</div>"
53420             );
53421             tpls.master.disableformats = true;
53422         }
53423
53424         if(!tpls.header){
53425             tpls.header = new Roo.Template(
53426                '<table border="0" cellspacing="0" cellpadding="0">',
53427                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53428                "</table>{splits}"
53429             );
53430             tpls.header.disableformats = true;
53431         }
53432         tpls.header.compile();
53433
53434         if(!tpls.hcell){
53435             tpls.hcell = new Roo.Template(
53436                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53437                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53438                 "</div></td>"
53439              );
53440              tpls.hcell.disableFormats = true;
53441         }
53442         tpls.hcell.compile();
53443
53444         if(!tpls.hsplit){
53445             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53446                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53447             tpls.hsplit.disableFormats = true;
53448         }
53449         tpls.hsplit.compile();
53450
53451         if(!tpls.body){
53452             tpls.body = new Roo.Template(
53453                '<table border="0" cellspacing="0" cellpadding="0">',
53454                "<tbody>{rows}</tbody>",
53455                "</table>"
53456             );
53457             tpls.body.disableFormats = true;
53458         }
53459         tpls.body.compile();
53460
53461         if(!tpls.row){
53462             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53463             tpls.row.disableFormats = true;
53464         }
53465         tpls.row.compile();
53466
53467         if(!tpls.cell){
53468             tpls.cell = new Roo.Template(
53469                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53470                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53471                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53472                 "</td>"
53473             );
53474             tpls.cell.disableFormats = true;
53475         }
53476         tpls.cell.compile();
53477
53478         this.templates = tpls;
53479     },
53480
53481     // remap these for backwards compat
53482     onColWidthChange : function(){
53483         this.updateColumns.apply(this, arguments);
53484     },
53485     onHeaderChange : function(){
53486         this.updateHeaders.apply(this, arguments);
53487     }, 
53488     onHiddenChange : function(){
53489         this.handleHiddenChange.apply(this, arguments);
53490     },
53491     onColumnMove : function(){
53492         this.handleColumnMove.apply(this, arguments);
53493     },
53494     onColumnLock : function(){
53495         this.handleLockChange.apply(this, arguments);
53496     },
53497
53498     onDataChange : function(){
53499         this.refresh();
53500         this.updateHeaderSortState();
53501     },
53502
53503     onClear : function(){
53504         this.refresh();
53505     },
53506
53507     onUpdate : function(ds, record){
53508         this.refreshRow(record);
53509     },
53510
53511     refreshRow : function(record){
53512         var ds = this.ds, index;
53513         if(typeof record == 'number'){
53514             index = record;
53515             record = ds.getAt(index);
53516         }else{
53517             index = ds.indexOf(record);
53518         }
53519         this.insertRows(ds, index, index, true);
53520         this.onRemove(ds, record, index+1, true);
53521         this.syncRowHeights(index, index);
53522         this.layout();
53523         this.fireEvent("rowupdated", this, index, record);
53524     },
53525
53526     onAdd : function(ds, records, index){
53527         this.insertRows(ds, index, index + (records.length-1));
53528     },
53529
53530     onRemove : function(ds, record, index, isUpdate){
53531         if(isUpdate !== true){
53532             this.fireEvent("beforerowremoved", this, index, record);
53533         }
53534         var bt = this.getBodyTable(), lt = this.getLockedTable();
53535         if(bt.rows[index]){
53536             bt.firstChild.removeChild(bt.rows[index]);
53537         }
53538         if(lt.rows[index]){
53539             lt.firstChild.removeChild(lt.rows[index]);
53540         }
53541         if(isUpdate !== true){
53542             this.stripeRows(index);
53543             this.syncRowHeights(index, index);
53544             this.layout();
53545             this.fireEvent("rowremoved", this, index, record);
53546         }
53547     },
53548
53549     onLoad : function(){
53550         this.scrollToTop();
53551     },
53552
53553     /**
53554      * Scrolls the grid to the top
53555      */
53556     scrollToTop : function(){
53557         if(this.scroller){
53558             this.scroller.dom.scrollTop = 0;
53559             this.syncScroll();
53560         }
53561     },
53562
53563     /**
53564      * Gets a panel in the header of the grid that can be used for toolbars etc.
53565      * After modifying the contents of this panel a call to grid.autoSize() may be
53566      * required to register any changes in size.
53567      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53568      * @return Roo.Element
53569      */
53570     getHeaderPanel : function(doShow){
53571         if(doShow){
53572             this.headerPanel.show();
53573         }
53574         return this.headerPanel;
53575     },
53576
53577     /**
53578      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53579      * After modifying the contents of this panel a call to grid.autoSize() may be
53580      * required to register any changes in size.
53581      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53582      * @return Roo.Element
53583      */
53584     getFooterPanel : function(doShow){
53585         if(doShow){
53586             this.footerPanel.show();
53587         }
53588         return this.footerPanel;
53589     },
53590
53591     initElements : function(){
53592         var E = Roo.Element;
53593         var el = this.grid.getGridEl().dom.firstChild;
53594         var cs = el.childNodes;
53595
53596         this.el = new E(el);
53597         
53598          this.focusEl = new E(el.firstChild);
53599         this.focusEl.swallowEvent("click", true);
53600         
53601         this.headerPanel = new E(cs[1]);
53602         this.headerPanel.enableDisplayMode("block");
53603
53604         this.scroller = new E(cs[2]);
53605         this.scrollSizer = new E(this.scroller.dom.firstChild);
53606
53607         this.lockedWrap = new E(cs[3]);
53608         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53609         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53610
53611         this.mainWrap = new E(cs[4]);
53612         this.mainHd = new E(this.mainWrap.dom.firstChild);
53613         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53614
53615         this.footerPanel = new E(cs[5]);
53616         this.footerPanel.enableDisplayMode("block");
53617
53618         this.resizeProxy = new E(cs[6]);
53619
53620         this.headerSelector = String.format(
53621            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53622            this.lockedHd.id, this.mainHd.id
53623         );
53624
53625         this.splitterSelector = String.format(
53626            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53627            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53628         );
53629     },
53630     idToCssName : function(s)
53631     {
53632         return s.replace(/[^a-z0-9]+/ig, '-');
53633     },
53634
53635     getHeaderCell : function(index){
53636         return Roo.DomQuery.select(this.headerSelector)[index];
53637     },
53638
53639     getHeaderCellMeasure : function(index){
53640         return this.getHeaderCell(index).firstChild;
53641     },
53642
53643     getHeaderCellText : function(index){
53644         return this.getHeaderCell(index).firstChild.firstChild;
53645     },
53646
53647     getLockedTable : function(){
53648         return this.lockedBody.dom.firstChild;
53649     },
53650
53651     getBodyTable : function(){
53652         return this.mainBody.dom.firstChild;
53653     },
53654
53655     getLockedRow : function(index){
53656         return this.getLockedTable().rows[index];
53657     },
53658
53659     getRow : function(index){
53660         return this.getBodyTable().rows[index];
53661     },
53662
53663     getRowComposite : function(index){
53664         if(!this.rowEl){
53665             this.rowEl = new Roo.CompositeElementLite();
53666         }
53667         var els = [], lrow, mrow;
53668         if(lrow = this.getLockedRow(index)){
53669             els.push(lrow);
53670         }
53671         if(mrow = this.getRow(index)){
53672             els.push(mrow);
53673         }
53674         this.rowEl.elements = els;
53675         return this.rowEl;
53676     },
53677     /**
53678      * Gets the 'td' of the cell
53679      * 
53680      * @param {Integer} rowIndex row to select
53681      * @param {Integer} colIndex column to select
53682      * 
53683      * @return {Object} 
53684      */
53685     getCell : function(rowIndex, colIndex){
53686         var locked = this.cm.getLockedCount();
53687         var source;
53688         if(colIndex < locked){
53689             source = this.lockedBody.dom.firstChild;
53690         }else{
53691             source = this.mainBody.dom.firstChild;
53692             colIndex -= locked;
53693         }
53694         return source.rows[rowIndex].childNodes[colIndex];
53695     },
53696
53697     getCellText : function(rowIndex, colIndex){
53698         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53699     },
53700
53701     getCellBox : function(cell){
53702         var b = this.fly(cell).getBox();
53703         if(Roo.isOpera){ // opera fails to report the Y
53704             b.y = cell.offsetTop + this.mainBody.getY();
53705         }
53706         return b;
53707     },
53708
53709     getCellIndex : function(cell){
53710         var id = String(cell.className).match(this.cellRE);
53711         if(id){
53712             return parseInt(id[1], 10);
53713         }
53714         return 0;
53715     },
53716
53717     findHeaderIndex : function(n){
53718         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53719         return r ? this.getCellIndex(r) : false;
53720     },
53721
53722     findHeaderCell : function(n){
53723         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53724         return r ? r : false;
53725     },
53726
53727     findRowIndex : function(n){
53728         if(!n){
53729             return false;
53730         }
53731         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53732         return r ? r.rowIndex : false;
53733     },
53734
53735     findCellIndex : function(node){
53736         var stop = this.el.dom;
53737         while(node && node != stop){
53738             if(this.findRE.test(node.className)){
53739                 return this.getCellIndex(node);
53740             }
53741             node = node.parentNode;
53742         }
53743         return false;
53744     },
53745
53746     getColumnId : function(index){
53747         return this.cm.getColumnId(index);
53748     },
53749
53750     getSplitters : function()
53751     {
53752         if(this.splitterSelector){
53753            return Roo.DomQuery.select(this.splitterSelector);
53754         }else{
53755             return null;
53756       }
53757     },
53758
53759     getSplitter : function(index){
53760         return this.getSplitters()[index];
53761     },
53762
53763     onRowOver : function(e, t){
53764         var row;
53765         if((row = this.findRowIndex(t)) !== false){
53766             this.getRowComposite(row).addClass("x-grid-row-over");
53767         }
53768     },
53769
53770     onRowOut : function(e, t){
53771         var row;
53772         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53773             this.getRowComposite(row).removeClass("x-grid-row-over");
53774         }
53775     },
53776
53777     renderHeaders : function(){
53778         var cm = this.cm;
53779         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53780         var cb = [], lb = [], sb = [], lsb = [], p = {};
53781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53782             p.cellId = "x-grid-hd-0-" + i;
53783             p.splitId = "x-grid-csplit-0-" + i;
53784             p.id = cm.getColumnId(i);
53785             p.title = cm.getColumnTooltip(i) || "";
53786             p.value = cm.getColumnHeader(i) || "";
53787             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53788             if(!cm.isLocked(i)){
53789                 cb[cb.length] = ct.apply(p);
53790                 sb[sb.length] = st.apply(p);
53791             }else{
53792                 lb[lb.length] = ct.apply(p);
53793                 lsb[lsb.length] = st.apply(p);
53794             }
53795         }
53796         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53797                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53798     },
53799
53800     updateHeaders : function(){
53801         var html = this.renderHeaders();
53802         this.lockedHd.update(html[0]);
53803         this.mainHd.update(html[1]);
53804     },
53805
53806     /**
53807      * Focuses the specified row.
53808      * @param {Number} row The row index
53809      */
53810     focusRow : function(row)
53811     {
53812         //Roo.log('GridView.focusRow');
53813         var x = this.scroller.dom.scrollLeft;
53814         this.focusCell(row, 0, false);
53815         this.scroller.dom.scrollLeft = x;
53816     },
53817
53818     /**
53819      * Focuses the specified cell.
53820      * @param {Number} row The row index
53821      * @param {Number} col The column index
53822      * @param {Boolean} hscroll false to disable horizontal scrolling
53823      */
53824     focusCell : function(row, col, hscroll)
53825     {
53826         //Roo.log('GridView.focusCell');
53827         var el = this.ensureVisible(row, col, hscroll);
53828         this.focusEl.alignTo(el, "tl-tl");
53829         if(Roo.isGecko){
53830             this.focusEl.focus();
53831         }else{
53832             this.focusEl.focus.defer(1, this.focusEl);
53833         }
53834     },
53835
53836     /**
53837      * Scrolls the specified cell into view
53838      * @param {Number} row The row index
53839      * @param {Number} col The column index
53840      * @param {Boolean} hscroll false to disable horizontal scrolling
53841      */
53842     ensureVisible : function(row, col, hscroll)
53843     {
53844         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53845         //return null; //disable for testing.
53846         if(typeof row != "number"){
53847             row = row.rowIndex;
53848         }
53849         if(row < 0 && row >= this.ds.getCount()){
53850             return  null;
53851         }
53852         col = (col !== undefined ? col : 0);
53853         var cm = this.grid.colModel;
53854         while(cm.isHidden(col)){
53855             col++;
53856         }
53857
53858         var el = this.getCell(row, col);
53859         if(!el){
53860             return null;
53861         }
53862         var c = this.scroller.dom;
53863
53864         var ctop = parseInt(el.offsetTop, 10);
53865         var cleft = parseInt(el.offsetLeft, 10);
53866         var cbot = ctop + el.offsetHeight;
53867         var cright = cleft + el.offsetWidth;
53868         
53869         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53870         var stop = parseInt(c.scrollTop, 10);
53871         var sleft = parseInt(c.scrollLeft, 10);
53872         var sbot = stop + ch;
53873         var sright = sleft + c.clientWidth;
53874         /*
53875         Roo.log('GridView.ensureVisible:' +
53876                 ' ctop:' + ctop +
53877                 ' c.clientHeight:' + c.clientHeight +
53878                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53879                 ' stop:' + stop +
53880                 ' cbot:' + cbot +
53881                 ' sbot:' + sbot +
53882                 ' ch:' + ch  
53883                 );
53884         */
53885         if(ctop < stop){
53886              c.scrollTop = ctop;
53887             //Roo.log("set scrolltop to ctop DISABLE?");
53888         }else if(cbot > sbot){
53889             //Roo.log("set scrolltop to cbot-ch");
53890             c.scrollTop = cbot-ch;
53891         }
53892         
53893         if(hscroll !== false){
53894             if(cleft < sleft){
53895                 c.scrollLeft = cleft;
53896             }else if(cright > sright){
53897                 c.scrollLeft = cright-c.clientWidth;
53898             }
53899         }
53900          
53901         return el;
53902     },
53903
53904     updateColumns : function(){
53905         this.grid.stopEditing();
53906         var cm = this.grid.colModel, colIds = this.getColumnIds();
53907         //var totalWidth = cm.getTotalWidth();
53908         var pos = 0;
53909         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53910             //if(cm.isHidden(i)) continue;
53911             var w = cm.getColumnWidth(i);
53912             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53913             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53914         }
53915         this.updateSplitters();
53916     },
53917
53918     generateRules : function(cm){
53919         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53920         Roo.util.CSS.removeStyleSheet(rulesId);
53921         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53922             var cid = cm.getColumnId(i);
53923             var align = '';
53924             if(cm.config[i].align){
53925                 align = 'text-align:'+cm.config[i].align+';';
53926             }
53927             var hidden = '';
53928             if(cm.isHidden(i)){
53929                 hidden = 'display:none;';
53930             }
53931             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53932             ruleBuf.push(
53933                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53934                     this.hdSelector, cid, " {\n", align, width, "}\n",
53935                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53936                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53937         }
53938         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53939     },
53940
53941     updateSplitters : function(){
53942         var cm = this.cm, s = this.getSplitters();
53943         if(s){ // splitters not created yet
53944             var pos = 0, locked = true;
53945             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53946                 if(cm.isHidden(i)) continue;
53947                 var w = cm.getColumnWidth(i); // make sure it's a number
53948                 if(!cm.isLocked(i) && locked){
53949                     pos = 0;
53950                     locked = false;
53951                 }
53952                 pos += w;
53953                 s[i].style.left = (pos-this.splitOffset) + "px";
53954             }
53955         }
53956     },
53957
53958     handleHiddenChange : function(colModel, colIndex, hidden){
53959         if(hidden){
53960             this.hideColumn(colIndex);
53961         }else{
53962             this.unhideColumn(colIndex);
53963         }
53964     },
53965
53966     hideColumn : function(colIndex){
53967         var cid = this.getColumnId(colIndex);
53968         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53969         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53970         if(Roo.isSafari){
53971             this.updateHeaders();
53972         }
53973         this.updateSplitters();
53974         this.layout();
53975     },
53976
53977     unhideColumn : function(colIndex){
53978         var cid = this.getColumnId(colIndex);
53979         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53980         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53981
53982         if(Roo.isSafari){
53983             this.updateHeaders();
53984         }
53985         this.updateSplitters();
53986         this.layout();
53987     },
53988
53989     insertRows : function(dm, firstRow, lastRow, isUpdate){
53990         if(firstRow == 0 && lastRow == dm.getCount()-1){
53991             this.refresh();
53992         }else{
53993             if(!isUpdate){
53994                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53995             }
53996             var s = this.getScrollState();
53997             var markup = this.renderRows(firstRow, lastRow);
53998             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53999             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54000             this.restoreScroll(s);
54001             if(!isUpdate){
54002                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54003                 this.syncRowHeights(firstRow, lastRow);
54004                 this.stripeRows(firstRow);
54005                 this.layout();
54006             }
54007         }
54008     },
54009
54010     bufferRows : function(markup, target, index){
54011         var before = null, trows = target.rows, tbody = target.tBodies[0];
54012         if(index < trows.length){
54013             before = trows[index];
54014         }
54015         var b = document.createElement("div");
54016         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54017         var rows = b.firstChild.rows;
54018         for(var i = 0, len = rows.length; i < len; i++){
54019             if(before){
54020                 tbody.insertBefore(rows[0], before);
54021             }else{
54022                 tbody.appendChild(rows[0]);
54023             }
54024         }
54025         b.innerHTML = "";
54026         b = null;
54027     },
54028
54029     deleteRows : function(dm, firstRow, lastRow){
54030         if(dm.getRowCount()<1){
54031             this.fireEvent("beforerefresh", this);
54032             this.mainBody.update("");
54033             this.lockedBody.update("");
54034             this.fireEvent("refresh", this);
54035         }else{
54036             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54037             var bt = this.getBodyTable();
54038             var tbody = bt.firstChild;
54039             var rows = bt.rows;
54040             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54041                 tbody.removeChild(rows[firstRow]);
54042             }
54043             this.stripeRows(firstRow);
54044             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54045         }
54046     },
54047
54048     updateRows : function(dataSource, firstRow, lastRow){
54049         var s = this.getScrollState();
54050         this.refresh();
54051         this.restoreScroll(s);
54052     },
54053
54054     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54055         if(!noRefresh){
54056            this.refresh();
54057         }
54058         this.updateHeaderSortState();
54059     },
54060
54061     getScrollState : function(){
54062         
54063         var sb = this.scroller.dom;
54064         return {left: sb.scrollLeft, top: sb.scrollTop};
54065     },
54066
54067     stripeRows : function(startRow){
54068         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54069             return;
54070         }
54071         startRow = startRow || 0;
54072         var rows = this.getBodyTable().rows;
54073         var lrows = this.getLockedTable().rows;
54074         var cls = ' x-grid-row-alt ';
54075         for(var i = startRow, len = rows.length; i < len; i++){
54076             var row = rows[i], lrow = lrows[i];
54077             var isAlt = ((i+1) % 2 == 0);
54078             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54079             if(isAlt == hasAlt){
54080                 continue;
54081             }
54082             if(isAlt){
54083                 row.className += " x-grid-row-alt";
54084             }else{
54085                 row.className = row.className.replace("x-grid-row-alt", "");
54086             }
54087             if(lrow){
54088                 lrow.className = row.className;
54089             }
54090         }
54091     },
54092
54093     restoreScroll : function(state){
54094         //Roo.log('GridView.restoreScroll');
54095         var sb = this.scroller.dom;
54096         sb.scrollLeft = state.left;
54097         sb.scrollTop = state.top;
54098         this.syncScroll();
54099     },
54100
54101     syncScroll : function(){
54102         //Roo.log('GridView.syncScroll');
54103         var sb = this.scroller.dom;
54104         var sh = this.mainHd.dom;
54105         var bs = this.mainBody.dom;
54106         var lv = this.lockedBody.dom;
54107         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54108         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54109     },
54110
54111     handleScroll : function(e){
54112         this.syncScroll();
54113         var sb = this.scroller.dom;
54114         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54115         e.stopEvent();
54116     },
54117
54118     handleWheel : function(e){
54119         var d = e.getWheelDelta();
54120         this.scroller.dom.scrollTop -= d*22;
54121         // set this here to prevent jumpy scrolling on large tables
54122         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54123         e.stopEvent();
54124     },
54125
54126     renderRows : function(startRow, endRow){
54127         // pull in all the crap needed to render rows
54128         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54129         var colCount = cm.getColumnCount();
54130
54131         if(ds.getCount() < 1){
54132             return ["", ""];
54133         }
54134
54135         // build a map for all the columns
54136         var cs = [];
54137         for(var i = 0; i < colCount; i++){
54138             var name = cm.getDataIndex(i);
54139             cs[i] = {
54140                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54141                 renderer : cm.getRenderer(i),
54142                 id : cm.getColumnId(i),
54143                 locked : cm.isLocked(i)
54144             };
54145         }
54146
54147         startRow = startRow || 0;
54148         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54149
54150         // records to render
54151         var rs = ds.getRange(startRow, endRow);
54152
54153         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54154     },
54155
54156     // As much as I hate to duplicate code, this was branched because FireFox really hates
54157     // [].join("") on strings. The performance difference was substantial enough to
54158     // branch this function
54159     doRender : Roo.isGecko ?
54160             function(cs, rs, ds, startRow, colCount, stripe){
54161                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54162                 // buffers
54163                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54164                 
54165                 var hasListener = this.grid.hasListener('rowclass');
54166                 var rowcfg = {};
54167                 for(var j = 0, len = rs.length; j < len; j++){
54168                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54169                     for(var i = 0; i < colCount; i++){
54170                         c = cs[i];
54171                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54172                         p.id = c.id;
54173                         p.css = p.attr = "";
54174                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54175                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54176                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54177                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54178                         }
54179                         var markup = ct.apply(p);
54180                         if(!c.locked){
54181                             cb+= markup;
54182                         }else{
54183                             lcb+= markup;
54184                         }
54185                     }
54186                     var alt = [];
54187                     if(stripe && ((rowIndex+1) % 2 == 0)){
54188                         alt.push("x-grid-row-alt")
54189                     }
54190                     if(r.dirty){
54191                         alt.push(  " x-grid-dirty-row");
54192                     }
54193                     rp.cells = lcb;
54194                     if(this.getRowClass){
54195                         alt.push(this.getRowClass(r, rowIndex));
54196                     }
54197                     if (hasListener) {
54198                         rowcfg = {
54199                              
54200                             record: r,
54201                             rowIndex : rowIndex,
54202                             rowClass : ''
54203                         }
54204                         this.grid.fireEvent('rowclass', this, rowcfg);
54205                         alt.push(rowcfg.rowClass);
54206                     }
54207                     rp.alt = alt.join(" ");
54208                     lbuf+= rt.apply(rp);
54209                     rp.cells = cb;
54210                     buf+=  rt.apply(rp);
54211                 }
54212                 return [lbuf, buf];
54213             } :
54214             function(cs, rs, ds, startRow, colCount, stripe){
54215                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54216                 // buffers
54217                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54218                 var hasListener = this.grid.hasListener('rowclass');
54219  
54220                 var rowcfg = {};
54221                 for(var j = 0, len = rs.length; j < len; j++){
54222                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54223                     for(var i = 0; i < colCount; i++){
54224                         c = cs[i];
54225                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54226                         p.id = c.id;
54227                         p.css = p.attr = "";
54228                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54229                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54230                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54231                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54232                         }
54233                         
54234                         var markup = ct.apply(p);
54235                         if(!c.locked){
54236                             cb[cb.length] = markup;
54237                         }else{
54238                             lcb[lcb.length] = markup;
54239                         }
54240                     }
54241                     var alt = [];
54242                     if(stripe && ((rowIndex+1) % 2 == 0)){
54243                         alt.push( "x-grid-row-alt");
54244                     }
54245                     if(r.dirty){
54246                         alt.push(" x-grid-dirty-row");
54247                     }
54248                     rp.cells = lcb;
54249                     if(this.getRowClass){
54250                         alt.push( this.getRowClass(r, rowIndex));
54251                     }
54252                     if (hasListener) {
54253                         rowcfg = {
54254                              
54255                             record: r,
54256                             rowIndex : rowIndex,
54257                             rowClass : ''
54258                         }
54259                         this.grid.fireEvent('rowclass', this, rowcfg);
54260                         alt.push(rowcfg.rowClass);
54261                     }
54262                     rp.alt = alt.join(" ");
54263                     rp.cells = lcb.join("");
54264                     lbuf[lbuf.length] = rt.apply(rp);
54265                     rp.cells = cb.join("");
54266                     buf[buf.length] =  rt.apply(rp);
54267                 }
54268                 return [lbuf.join(""), buf.join("")];
54269             },
54270
54271     renderBody : function(){
54272         var markup = this.renderRows();
54273         var bt = this.templates.body;
54274         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54275     },
54276
54277     /**
54278      * Refreshes the grid
54279      * @param {Boolean} headersToo
54280      */
54281     refresh : function(headersToo){
54282         this.fireEvent("beforerefresh", this);
54283         this.grid.stopEditing();
54284         var result = this.renderBody();
54285         this.lockedBody.update(result[0]);
54286         this.mainBody.update(result[1]);
54287         if(headersToo === true){
54288             this.updateHeaders();
54289             this.updateColumns();
54290             this.updateSplitters();
54291             this.updateHeaderSortState();
54292         }
54293         this.syncRowHeights();
54294         this.layout();
54295         this.fireEvent("refresh", this);
54296     },
54297
54298     handleColumnMove : function(cm, oldIndex, newIndex){
54299         this.indexMap = null;
54300         var s = this.getScrollState();
54301         this.refresh(true);
54302         this.restoreScroll(s);
54303         this.afterMove(newIndex);
54304     },
54305
54306     afterMove : function(colIndex){
54307         if(this.enableMoveAnim && Roo.enableFx){
54308             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54309         }
54310         // if multisort - fix sortOrder, and reload..
54311         if (this.grid.dataSource.multiSort) {
54312             // the we can call sort again..
54313             var dm = this.grid.dataSource;
54314             var cm = this.grid.colModel;
54315             var so = [];
54316             for(var i = 0; i < cm.config.length; i++ ) {
54317                 
54318                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54319                     continue; // dont' bother, it's not in sort list or being set.
54320                 }
54321                 
54322                 so.push(cm.config[i].dataIndex);
54323             };
54324             dm.sortOrder = so;
54325             dm.load(dm.lastOptions);
54326             
54327             
54328         }
54329         
54330     },
54331
54332     updateCell : function(dm, rowIndex, dataIndex){
54333         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54334         if(typeof colIndex == "undefined"){ // not present in grid
54335             return;
54336         }
54337         var cm = this.grid.colModel;
54338         var cell = this.getCell(rowIndex, colIndex);
54339         var cellText = this.getCellText(rowIndex, colIndex);
54340
54341         var p = {
54342             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54343             id : cm.getColumnId(colIndex),
54344             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54345         };
54346         var renderer = cm.getRenderer(colIndex);
54347         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54348         if(typeof val == "undefined" || val === "") val = "&#160;";
54349         cellText.innerHTML = val;
54350         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54351         this.syncRowHeights(rowIndex, rowIndex);
54352     },
54353
54354     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54355         var maxWidth = 0;
54356         if(this.grid.autoSizeHeaders){
54357             var h = this.getHeaderCellMeasure(colIndex);
54358             maxWidth = Math.max(maxWidth, h.scrollWidth);
54359         }
54360         var tb, index;
54361         if(this.cm.isLocked(colIndex)){
54362             tb = this.getLockedTable();
54363             index = colIndex;
54364         }else{
54365             tb = this.getBodyTable();
54366             index = colIndex - this.cm.getLockedCount();
54367         }
54368         if(tb && tb.rows){
54369             var rows = tb.rows;
54370             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54371             for(var i = 0; i < stopIndex; i++){
54372                 var cell = rows[i].childNodes[index].firstChild;
54373                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54374             }
54375         }
54376         return maxWidth + /*margin for error in IE*/ 5;
54377     },
54378     /**
54379      * Autofit a column to its content.
54380      * @param {Number} colIndex
54381      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54382      */
54383      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54384          if(this.cm.isHidden(colIndex)){
54385              return; // can't calc a hidden column
54386          }
54387         if(forceMinSize){
54388             var cid = this.cm.getColumnId(colIndex);
54389             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54390            if(this.grid.autoSizeHeaders){
54391                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54392            }
54393         }
54394         var newWidth = this.calcColumnWidth(colIndex);
54395         this.cm.setColumnWidth(colIndex,
54396             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54397         if(!suppressEvent){
54398             this.grid.fireEvent("columnresize", colIndex, newWidth);
54399         }
54400     },
54401
54402     /**
54403      * Autofits all columns to their content and then expands to fit any extra space in the grid
54404      */
54405      autoSizeColumns : function(){
54406         var cm = this.grid.colModel;
54407         var colCount = cm.getColumnCount();
54408         for(var i = 0; i < colCount; i++){
54409             this.autoSizeColumn(i, true, true);
54410         }
54411         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54412             this.fitColumns();
54413         }else{
54414             this.updateColumns();
54415             this.layout();
54416         }
54417     },
54418
54419     /**
54420      * Autofits all columns to the grid's width proportionate with their current size
54421      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54422      */
54423     fitColumns : function(reserveScrollSpace){
54424         var cm = this.grid.colModel;
54425         var colCount = cm.getColumnCount();
54426         var cols = [];
54427         var width = 0;
54428         var i, w;
54429         for (i = 0; i < colCount; i++){
54430             if(!cm.isHidden(i) && !cm.isFixed(i)){
54431                 w = cm.getColumnWidth(i);
54432                 cols.push(i);
54433                 cols.push(w);
54434                 width += w;
54435             }
54436         }
54437         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54438         if(reserveScrollSpace){
54439             avail -= 17;
54440         }
54441         var frac = (avail - cm.getTotalWidth())/width;
54442         while (cols.length){
54443             w = cols.pop();
54444             i = cols.pop();
54445             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54446         }
54447         this.updateColumns();
54448         this.layout();
54449     },
54450
54451     onRowSelect : function(rowIndex){
54452         var row = this.getRowComposite(rowIndex);
54453         row.addClass("x-grid-row-selected");
54454     },
54455
54456     onRowDeselect : function(rowIndex){
54457         var row = this.getRowComposite(rowIndex);
54458         row.removeClass("x-grid-row-selected");
54459     },
54460
54461     onCellSelect : function(row, col){
54462         var cell = this.getCell(row, col);
54463         if(cell){
54464             Roo.fly(cell).addClass("x-grid-cell-selected");
54465         }
54466     },
54467
54468     onCellDeselect : function(row, col){
54469         var cell = this.getCell(row, col);
54470         if(cell){
54471             Roo.fly(cell).removeClass("x-grid-cell-selected");
54472         }
54473     },
54474
54475     updateHeaderSortState : function(){
54476         
54477         // sort state can be single { field: xxx, direction : yyy}
54478         // or   { xxx=>ASC , yyy : DESC ..... }
54479         
54480         var mstate = {};
54481         if (!this.ds.multiSort) { 
54482             var state = this.ds.getSortState();
54483             if(!state){
54484                 return;
54485             }
54486             mstate[state.field] = state.direction;
54487             // FIXME... - this is not used here.. but might be elsewhere..
54488             this.sortState = state;
54489             
54490         } else {
54491             mstate = this.ds.sortToggle;
54492         }
54493         //remove existing sort classes..
54494         
54495         var sc = this.sortClasses;
54496         var hds = this.el.select(this.headerSelector).removeClass(sc);
54497         
54498         for(var f in mstate) {
54499         
54500             var sortColumn = this.cm.findColumnIndex(f);
54501             
54502             if(sortColumn != -1){
54503                 var sortDir = mstate[f];        
54504                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54505             }
54506         }
54507         
54508          
54509         
54510     },
54511
54512
54513     handleHeaderClick : function(g, index,e){
54514         
54515         Roo.log("header click");
54516         
54517         if (Roo.isTouch) {
54518             // touch events on header are handled by context
54519             this.handleHdCtx(g,index,e);
54520             return;
54521         }
54522         
54523         
54524         if(this.headersDisabled){
54525             return;
54526         }
54527         var dm = g.dataSource, cm = g.colModel;
54528         if(!cm.isSortable(index)){
54529             return;
54530         }
54531         g.stopEditing();
54532         
54533         if (dm.multiSort) {
54534             // update the sortOrder
54535             var so = [];
54536             for(var i = 0; i < cm.config.length; i++ ) {
54537                 
54538                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54539                     continue; // dont' bother, it's not in sort list or being set.
54540                 }
54541                 
54542                 so.push(cm.config[i].dataIndex);
54543             };
54544             dm.sortOrder = so;
54545         }
54546         
54547         
54548         dm.sort(cm.getDataIndex(index));
54549     },
54550
54551
54552     destroy : function(){
54553         if(this.colMenu){
54554             this.colMenu.removeAll();
54555             Roo.menu.MenuMgr.unregister(this.colMenu);
54556             this.colMenu.getEl().remove();
54557             delete this.colMenu;
54558         }
54559         if(this.hmenu){
54560             this.hmenu.removeAll();
54561             Roo.menu.MenuMgr.unregister(this.hmenu);
54562             this.hmenu.getEl().remove();
54563             delete this.hmenu;
54564         }
54565         if(this.grid.enableColumnMove){
54566             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54567             if(dds){
54568                 for(var dd in dds){
54569                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54570                         var elid = dds[dd].dragElId;
54571                         dds[dd].unreg();
54572                         Roo.get(elid).remove();
54573                     } else if(dds[dd].config.isTarget){
54574                         dds[dd].proxyTop.remove();
54575                         dds[dd].proxyBottom.remove();
54576                         dds[dd].unreg();
54577                     }
54578                     if(Roo.dd.DDM.locationCache[dd]){
54579                         delete Roo.dd.DDM.locationCache[dd];
54580                     }
54581                 }
54582                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54583             }
54584         }
54585         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54586         this.bind(null, null);
54587         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54588     },
54589
54590     handleLockChange : function(){
54591         this.refresh(true);
54592     },
54593
54594     onDenyColumnLock : function(){
54595
54596     },
54597
54598     onDenyColumnHide : function(){
54599
54600     },
54601
54602     handleHdMenuClick : function(item){
54603         var index = this.hdCtxIndex;
54604         var cm = this.cm, ds = this.ds;
54605         switch(item.id){
54606             case "asc":
54607                 ds.sort(cm.getDataIndex(index), "ASC");
54608                 break;
54609             case "desc":
54610                 ds.sort(cm.getDataIndex(index), "DESC");
54611                 break;
54612             case "lock":
54613                 var lc = cm.getLockedCount();
54614                 if(cm.getColumnCount(true) <= lc+1){
54615                     this.onDenyColumnLock();
54616                     return;
54617                 }
54618                 if(lc != index){
54619                     cm.setLocked(index, true, true);
54620                     cm.moveColumn(index, lc);
54621                     this.grid.fireEvent("columnmove", index, lc);
54622                 }else{
54623                     cm.setLocked(index, true);
54624                 }
54625             break;
54626             case "unlock":
54627                 var lc = cm.getLockedCount();
54628                 if((lc-1) != index){
54629                     cm.setLocked(index, false, true);
54630                     cm.moveColumn(index, lc-1);
54631                     this.grid.fireEvent("columnmove", index, lc-1);
54632                 }else{
54633                     cm.setLocked(index, false);
54634                 }
54635             break;
54636             case 'wider': // used to expand cols on touch..
54637             case 'narrow':
54638                 var cw = cm.getColumnWidth(index);
54639                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54640                 cw = Math.max(0, cw);
54641                 cw = Math.min(cw,4000);
54642                 cm.setColumnWidth(index, cw);
54643                 break;
54644                 
54645             default:
54646                 index = cm.getIndexById(item.id.substr(4));
54647                 if(index != -1){
54648                     if(item.checked && cm.getColumnCount(true) <= 1){
54649                         this.onDenyColumnHide();
54650                         return false;
54651                     }
54652                     cm.setHidden(index, item.checked);
54653                 }
54654         }
54655         return true;
54656     },
54657
54658     beforeColMenuShow : function(){
54659         var cm = this.cm,  colCount = cm.getColumnCount();
54660         this.colMenu.removeAll();
54661         for(var i = 0; i < colCount; i++){
54662             this.colMenu.add(new Roo.menu.CheckItem({
54663                 id: "col-"+cm.getColumnId(i),
54664                 text: cm.getColumnHeader(i),
54665                 checked: !cm.isHidden(i),
54666                 hideOnClick:false
54667             }));
54668         }
54669     },
54670
54671     handleHdCtx : function(g, index, e){
54672         e.stopEvent();
54673         var hd = this.getHeaderCell(index);
54674         this.hdCtxIndex = index;
54675         var ms = this.hmenu.items, cm = this.cm;
54676         ms.get("asc").setDisabled(!cm.isSortable(index));
54677         ms.get("desc").setDisabled(!cm.isSortable(index));
54678         if(this.grid.enableColLock !== false){
54679             ms.get("lock").setDisabled(cm.isLocked(index));
54680             ms.get("unlock").setDisabled(!cm.isLocked(index));
54681         }
54682         this.hmenu.show(hd, "tl-bl");
54683     },
54684
54685     handleHdOver : function(e){
54686         var hd = this.findHeaderCell(e.getTarget());
54687         if(hd && !this.headersDisabled){
54688             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54689                this.fly(hd).addClass("x-grid-hd-over");
54690             }
54691         }
54692     },
54693
54694     handleHdOut : function(e){
54695         var hd = this.findHeaderCell(e.getTarget());
54696         if(hd){
54697             this.fly(hd).removeClass("x-grid-hd-over");
54698         }
54699     },
54700
54701     handleSplitDblClick : function(e, t){
54702         var i = this.getCellIndex(t);
54703         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54704             this.autoSizeColumn(i, true);
54705             this.layout();
54706         }
54707     },
54708
54709     render : function(){
54710
54711         var cm = this.cm;
54712         var colCount = cm.getColumnCount();
54713
54714         if(this.grid.monitorWindowResize === true){
54715             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54716         }
54717         var header = this.renderHeaders();
54718         var body = this.templates.body.apply({rows:""});
54719         var html = this.templates.master.apply({
54720             lockedBody: body,
54721             body: body,
54722             lockedHeader: header[0],
54723             header: header[1]
54724         });
54725
54726         //this.updateColumns();
54727
54728         this.grid.getGridEl().dom.innerHTML = html;
54729
54730         this.initElements();
54731         
54732         // a kludge to fix the random scolling effect in webkit
54733         this.el.on("scroll", function() {
54734             this.el.dom.scrollTop=0; // hopefully not recursive..
54735         },this);
54736
54737         this.scroller.on("scroll", this.handleScroll, this);
54738         this.lockedBody.on("mousewheel", this.handleWheel, this);
54739         this.mainBody.on("mousewheel", this.handleWheel, this);
54740
54741         this.mainHd.on("mouseover", this.handleHdOver, this);
54742         this.mainHd.on("mouseout", this.handleHdOut, this);
54743         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54744                 {delegate: "."+this.splitClass});
54745
54746         this.lockedHd.on("mouseover", this.handleHdOver, this);
54747         this.lockedHd.on("mouseout", this.handleHdOut, this);
54748         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54749                 {delegate: "."+this.splitClass});
54750
54751         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54752             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54753         }
54754
54755         this.updateSplitters();
54756
54757         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54758             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54759             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54760         }
54761
54762         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54763             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54764             this.hmenu.add(
54765                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54766                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54767             );
54768             if(this.grid.enableColLock !== false){
54769                 this.hmenu.add('-',
54770                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54771                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54772                 );
54773             }
54774             if (Roo.isTouch) {
54775                  this.hmenu.add('-',
54776                     {id:"wider", text: this.columnsWiderText},
54777                     {id:"narrow", text: this.columnsNarrowText }
54778                 );
54779                 
54780                  
54781             }
54782             
54783             if(this.grid.enableColumnHide !== false){
54784
54785                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54786                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54787                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54788
54789                 this.hmenu.add('-',
54790                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54791                 );
54792             }
54793             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54794
54795             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54796         }
54797
54798         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54799             this.dd = new Roo.grid.GridDragZone(this.grid, {
54800                 ddGroup : this.grid.ddGroup || 'GridDD'
54801             });
54802             
54803         }
54804
54805         /*
54806         for(var i = 0; i < colCount; i++){
54807             if(cm.isHidden(i)){
54808                 this.hideColumn(i);
54809             }
54810             if(cm.config[i].align){
54811                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54812                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54813             }
54814         }*/
54815         
54816         this.updateHeaderSortState();
54817
54818         this.beforeInitialResize();
54819         this.layout(true);
54820
54821         // two part rendering gives faster view to the user
54822         this.renderPhase2.defer(1, this);
54823     },
54824
54825     renderPhase2 : function(){
54826         // render the rows now
54827         this.refresh();
54828         if(this.grid.autoSizeColumns){
54829             this.autoSizeColumns();
54830         }
54831     },
54832
54833     beforeInitialResize : function(){
54834
54835     },
54836
54837     onColumnSplitterMoved : function(i, w){
54838         this.userResized = true;
54839         var cm = this.grid.colModel;
54840         cm.setColumnWidth(i, w, true);
54841         var cid = cm.getColumnId(i);
54842         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54843         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54844         this.updateSplitters();
54845         this.layout();
54846         this.grid.fireEvent("columnresize", i, w);
54847     },
54848
54849     syncRowHeights : function(startIndex, endIndex){
54850         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54851             startIndex = startIndex || 0;
54852             var mrows = this.getBodyTable().rows;
54853             var lrows = this.getLockedTable().rows;
54854             var len = mrows.length-1;
54855             endIndex = Math.min(endIndex || len, len);
54856             for(var i = startIndex; i <= endIndex; i++){
54857                 var m = mrows[i], l = lrows[i];
54858                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54859                 m.style.height = l.style.height = h + "px";
54860             }
54861         }
54862     },
54863
54864     layout : function(initialRender, is2ndPass){
54865         var g = this.grid;
54866         var auto = g.autoHeight;
54867         var scrollOffset = 16;
54868         var c = g.getGridEl(), cm = this.cm,
54869                 expandCol = g.autoExpandColumn,
54870                 gv = this;
54871         //c.beginMeasure();
54872
54873         if(!c.dom.offsetWidth){ // display:none?
54874             if(initialRender){
54875                 this.lockedWrap.show();
54876                 this.mainWrap.show();
54877             }
54878             return;
54879         }
54880
54881         var hasLock = this.cm.isLocked(0);
54882
54883         var tbh = this.headerPanel.getHeight();
54884         var bbh = this.footerPanel.getHeight();
54885
54886         if(auto){
54887             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54888             var newHeight = ch + c.getBorderWidth("tb");
54889             if(g.maxHeight){
54890                 newHeight = Math.min(g.maxHeight, newHeight);
54891             }
54892             c.setHeight(newHeight);
54893         }
54894
54895         if(g.autoWidth){
54896             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54897         }
54898
54899         var s = this.scroller;
54900
54901         var csize = c.getSize(true);
54902
54903         this.el.setSize(csize.width, csize.height);
54904
54905         this.headerPanel.setWidth(csize.width);
54906         this.footerPanel.setWidth(csize.width);
54907
54908         var hdHeight = this.mainHd.getHeight();
54909         var vw = csize.width;
54910         var vh = csize.height - (tbh + bbh);
54911
54912         s.setSize(vw, vh);
54913
54914         var bt = this.getBodyTable();
54915         var ltWidth = hasLock ?
54916                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54917
54918         var scrollHeight = bt.offsetHeight;
54919         var scrollWidth = ltWidth + bt.offsetWidth;
54920         var vscroll = false, hscroll = false;
54921
54922         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54923
54924         var lw = this.lockedWrap, mw = this.mainWrap;
54925         var lb = this.lockedBody, mb = this.mainBody;
54926
54927         setTimeout(function(){
54928             var t = s.dom.offsetTop;
54929             var w = s.dom.clientWidth,
54930                 h = s.dom.clientHeight;
54931
54932             lw.setTop(t);
54933             lw.setSize(ltWidth, h);
54934
54935             mw.setLeftTop(ltWidth, t);
54936             mw.setSize(w-ltWidth, h);
54937
54938             lb.setHeight(h-hdHeight);
54939             mb.setHeight(h-hdHeight);
54940
54941             if(is2ndPass !== true && !gv.userResized && expandCol){
54942                 // high speed resize without full column calculation
54943                 
54944                 var ci = cm.getIndexById(expandCol);
54945                 if (ci < 0) {
54946                     ci = cm.findColumnIndex(expandCol);
54947                 }
54948                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54949                 var expandId = cm.getColumnId(ci);
54950                 var  tw = cm.getTotalWidth(false);
54951                 var currentWidth = cm.getColumnWidth(ci);
54952                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54953                 if(currentWidth != cw){
54954                     cm.setColumnWidth(ci, cw, true);
54955                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54956                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54957                     gv.updateSplitters();
54958                     gv.layout(false, true);
54959                 }
54960             }
54961
54962             if(initialRender){
54963                 lw.show();
54964                 mw.show();
54965             }
54966             //c.endMeasure();
54967         }, 10);
54968     },
54969
54970     onWindowResize : function(){
54971         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54972             return;
54973         }
54974         this.layout();
54975     },
54976
54977     appendFooter : function(parentEl){
54978         return null;
54979     },
54980
54981     sortAscText : "Sort Ascending",
54982     sortDescText : "Sort Descending",
54983     lockText : "Lock Column",
54984     unlockText : "Unlock Column",
54985     columnsText : "Columns",
54986  
54987     columnsWiderText : "Wider",
54988     columnsNarrowText : "Thinner"
54989 });
54990
54991
54992 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54993     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54994     this.proxy.el.addClass('x-grid3-col-dd');
54995 };
54996
54997 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54998     handleMouseDown : function(e){
54999
55000     },
55001
55002     callHandleMouseDown : function(e){
55003         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55004     }
55005 });
55006 /*
55007  * Based on:
55008  * Ext JS Library 1.1.1
55009  * Copyright(c) 2006-2007, Ext JS, LLC.
55010  *
55011  * Originally Released Under LGPL - original licence link has changed is not relivant.
55012  *
55013  * Fork - LGPL
55014  * <script type="text/javascript">
55015  */
55016  
55017 // private
55018 // This is a support class used internally by the Grid components
55019 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55020     this.grid = grid;
55021     this.view = grid.getView();
55022     this.proxy = this.view.resizeProxy;
55023     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55024         "gridSplitters" + this.grid.getGridEl().id, {
55025         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55026     });
55027     this.setHandleElId(Roo.id(hd));
55028     this.setOuterHandleElId(Roo.id(hd2));
55029     this.scroll = false;
55030 };
55031 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55032     fly: Roo.Element.fly,
55033
55034     b4StartDrag : function(x, y){
55035         this.view.headersDisabled = true;
55036         this.proxy.setHeight(this.view.mainWrap.getHeight());
55037         var w = this.cm.getColumnWidth(this.cellIndex);
55038         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55039         this.resetConstraints();
55040         this.setXConstraint(minw, 1000);
55041         this.setYConstraint(0, 0);
55042         this.minX = x - minw;
55043         this.maxX = x + 1000;
55044         this.startPos = x;
55045         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55046     },
55047
55048
55049     handleMouseDown : function(e){
55050         ev = Roo.EventObject.setEvent(e);
55051         var t = this.fly(ev.getTarget());
55052         if(t.hasClass("x-grid-split")){
55053             this.cellIndex = this.view.getCellIndex(t.dom);
55054             this.split = t.dom;
55055             this.cm = this.grid.colModel;
55056             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55057                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55058             }
55059         }
55060     },
55061
55062     endDrag : function(e){
55063         this.view.headersDisabled = false;
55064         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55065         var diff = endX - this.startPos;
55066         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55067     },
55068
55069     autoOffset : function(){
55070         this.setDelta(0,0);
55071     }
55072 });/*
55073  * Based on:
55074  * Ext JS Library 1.1.1
55075  * Copyright(c) 2006-2007, Ext JS, LLC.
55076  *
55077  * Originally Released Under LGPL - original licence link has changed is not relivant.
55078  *
55079  * Fork - LGPL
55080  * <script type="text/javascript">
55081  */
55082  
55083 // private
55084 // This is a support class used internally by the Grid components
55085 Roo.grid.GridDragZone = function(grid, config){
55086     this.view = grid.getView();
55087     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55088     if(this.view.lockedBody){
55089         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55090         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55091     }
55092     this.scroll = false;
55093     this.grid = grid;
55094     this.ddel = document.createElement('div');
55095     this.ddel.className = 'x-grid-dd-wrap';
55096 };
55097
55098 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55099     ddGroup : "GridDD",
55100
55101     getDragData : function(e){
55102         var t = Roo.lib.Event.getTarget(e);
55103         var rowIndex = this.view.findRowIndex(t);
55104         var sm = this.grid.selModel;
55105             
55106         //Roo.log(rowIndex);
55107         
55108         if (sm.getSelectedCell) {
55109             // cell selection..
55110             if (!sm.getSelectedCell()) {
55111                 return false;
55112             }
55113             if (rowIndex != sm.getSelectedCell()[0]) {
55114                 return false;
55115             }
55116         
55117         }
55118         
55119         if(rowIndex !== false){
55120             
55121             // if editorgrid.. 
55122             
55123             
55124             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55125                
55126             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55127               //  
55128             //}
55129             if (e.hasModifier()){
55130                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55131             }
55132             
55133             Roo.log("getDragData");
55134             
55135             return {
55136                 grid: this.grid,
55137                 ddel: this.ddel,
55138                 rowIndex: rowIndex,
55139                 selections:sm.getSelections ? sm.getSelections() : (
55140                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55141                 )
55142             };
55143         }
55144         return false;
55145     },
55146
55147     onInitDrag : function(e){
55148         var data = this.dragData;
55149         this.ddel.innerHTML = this.grid.getDragDropText();
55150         this.proxy.update(this.ddel);
55151         // fire start drag?
55152     },
55153
55154     afterRepair : function(){
55155         this.dragging = false;
55156     },
55157
55158     getRepairXY : function(e, data){
55159         return false;
55160     },
55161
55162     onEndDrag : function(data, e){
55163         // fire end drag?
55164     },
55165
55166     onValidDrop : function(dd, e, id){
55167         // fire drag drop?
55168         this.hideProxy();
55169     },
55170
55171     beforeInvalidDrop : function(e, id){
55172
55173     }
55174 });/*
55175  * Based on:
55176  * Ext JS Library 1.1.1
55177  * Copyright(c) 2006-2007, Ext JS, LLC.
55178  *
55179  * Originally Released Under LGPL - original licence link has changed is not relivant.
55180  *
55181  * Fork - LGPL
55182  * <script type="text/javascript">
55183  */
55184  
55185
55186 /**
55187  * @class Roo.grid.ColumnModel
55188  * @extends Roo.util.Observable
55189  * This is the default implementation of a ColumnModel used by the Grid. It defines
55190  * the columns in the grid.
55191  * <br>Usage:<br>
55192  <pre><code>
55193  var colModel = new Roo.grid.ColumnModel([
55194         {header: "Ticker", width: 60, sortable: true, locked: true},
55195         {header: "Company Name", width: 150, sortable: true},
55196         {header: "Market Cap.", width: 100, sortable: true},
55197         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55198         {header: "Employees", width: 100, sortable: true, resizable: false}
55199  ]);
55200  </code></pre>
55201  * <p>
55202  
55203  * The config options listed for this class are options which may appear in each
55204  * individual column definition.
55205  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55206  * @constructor
55207  * @param {Object} config An Array of column config objects. See this class's
55208  * config objects for details.
55209 */
55210 Roo.grid.ColumnModel = function(config){
55211         /**
55212      * The config passed into the constructor
55213      */
55214     this.config = config;
55215     this.lookup = {};
55216
55217     // if no id, create one
55218     // if the column does not have a dataIndex mapping,
55219     // map it to the order it is in the config
55220     for(var i = 0, len = config.length; i < len; i++){
55221         var c = config[i];
55222         if(typeof c.dataIndex == "undefined"){
55223             c.dataIndex = i;
55224         }
55225         if(typeof c.renderer == "string"){
55226             c.renderer = Roo.util.Format[c.renderer];
55227         }
55228         if(typeof c.id == "undefined"){
55229             c.id = Roo.id();
55230         }
55231         if(c.editor && c.editor.xtype){
55232             c.editor  = Roo.factory(c.editor, Roo.grid);
55233         }
55234         if(c.editor && c.editor.isFormField){
55235             c.editor = new Roo.grid.GridEditor(c.editor);
55236         }
55237         this.lookup[c.id] = c;
55238     }
55239
55240     /**
55241      * The width of columns which have no width specified (defaults to 100)
55242      * @type Number
55243      */
55244     this.defaultWidth = 100;
55245
55246     /**
55247      * Default sortable of columns which have no sortable specified (defaults to false)
55248      * @type Boolean
55249      */
55250     this.defaultSortable = false;
55251
55252     this.addEvents({
55253         /**
55254              * @event widthchange
55255              * Fires when the width of a column changes.
55256              * @param {ColumnModel} this
55257              * @param {Number} columnIndex The column index
55258              * @param {Number} newWidth The new width
55259              */
55260             "widthchange": true,
55261         /**
55262              * @event headerchange
55263              * Fires when the text of a header changes.
55264              * @param {ColumnModel} this
55265              * @param {Number} columnIndex The column index
55266              * @param {Number} newText The new header text
55267              */
55268             "headerchange": true,
55269         /**
55270              * @event hiddenchange
55271              * Fires when a column is hidden or "unhidden".
55272              * @param {ColumnModel} this
55273              * @param {Number} columnIndex The column index
55274              * @param {Boolean} hidden true if hidden, false otherwise
55275              */
55276             "hiddenchange": true,
55277             /**
55278          * @event columnmoved
55279          * Fires when a column is moved.
55280          * @param {ColumnModel} this
55281          * @param {Number} oldIndex
55282          * @param {Number} newIndex
55283          */
55284         "columnmoved" : true,
55285         /**
55286          * @event columlockchange
55287          * Fires when a column's locked state is changed
55288          * @param {ColumnModel} this
55289          * @param {Number} colIndex
55290          * @param {Boolean} locked true if locked
55291          */
55292         "columnlockchange" : true
55293     });
55294     Roo.grid.ColumnModel.superclass.constructor.call(this);
55295 };
55296 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55297     /**
55298      * @cfg {String} header The header text to display in the Grid view.
55299      */
55300     /**
55301      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55302      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55303      * specified, the column's index is used as an index into the Record's data Array.
55304      */
55305     /**
55306      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55307      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55308      */
55309     /**
55310      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55311      * Defaults to the value of the {@link #defaultSortable} property.
55312      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55313      */
55314     /**
55315      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55316      */
55317     /**
55318      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55319      */
55320     /**
55321      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55322      */
55323     /**
55324      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55325      */
55326     /**
55327      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55328      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55329      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55330      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55331      */
55332        /**
55333      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55334      */
55335     /**
55336      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55337      */
55338     /**
55339      * @cfg {String} cursor (Optional)
55340      */
55341     /**
55342      * @cfg {String} tooltip (Optional)
55343      */
55344     /**
55345      * Returns the id of the column at the specified index.
55346      * @param {Number} index The column index
55347      * @return {String} the id
55348      */
55349     getColumnId : function(index){
55350         return this.config[index].id;
55351     },
55352
55353     /**
55354      * Returns the column for a specified id.
55355      * @param {String} id The column id
55356      * @return {Object} the column
55357      */
55358     getColumnById : function(id){
55359         return this.lookup[id];
55360     },
55361
55362     
55363     /**
55364      * Returns the column for a specified dataIndex.
55365      * @param {String} dataIndex The column dataIndex
55366      * @return {Object|Boolean} the column or false if not found
55367      */
55368     getColumnByDataIndex: function(dataIndex){
55369         var index = this.findColumnIndex(dataIndex);
55370         return index > -1 ? this.config[index] : false;
55371     },
55372     
55373     /**
55374      * Returns the index for a specified column id.
55375      * @param {String} id The column id
55376      * @return {Number} the index, or -1 if not found
55377      */
55378     getIndexById : function(id){
55379         for(var i = 0, len = this.config.length; i < len; i++){
55380             if(this.config[i].id == id){
55381                 return i;
55382             }
55383         }
55384         return -1;
55385     },
55386     
55387     /**
55388      * Returns the index for a specified column dataIndex.
55389      * @param {String} dataIndex The column dataIndex
55390      * @return {Number} the index, or -1 if not found
55391      */
55392     
55393     findColumnIndex : function(dataIndex){
55394         for(var i = 0, len = this.config.length; i < len; i++){
55395             if(this.config[i].dataIndex == dataIndex){
55396                 return i;
55397             }
55398         }
55399         return -1;
55400     },
55401     
55402     
55403     moveColumn : function(oldIndex, newIndex){
55404         var c = this.config[oldIndex];
55405         this.config.splice(oldIndex, 1);
55406         this.config.splice(newIndex, 0, c);
55407         this.dataMap = null;
55408         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55409     },
55410
55411     isLocked : function(colIndex){
55412         return this.config[colIndex].locked === true;
55413     },
55414
55415     setLocked : function(colIndex, value, suppressEvent){
55416         if(this.isLocked(colIndex) == value){
55417             return;
55418         }
55419         this.config[colIndex].locked = value;
55420         if(!suppressEvent){
55421             this.fireEvent("columnlockchange", this, colIndex, value);
55422         }
55423     },
55424
55425     getTotalLockedWidth : function(){
55426         var totalWidth = 0;
55427         for(var i = 0; i < this.config.length; i++){
55428             if(this.isLocked(i) && !this.isHidden(i)){
55429                 this.totalWidth += this.getColumnWidth(i);
55430             }
55431         }
55432         return totalWidth;
55433     },
55434
55435     getLockedCount : function(){
55436         for(var i = 0, len = this.config.length; i < len; i++){
55437             if(!this.isLocked(i)){
55438                 return i;
55439             }
55440         }
55441     },
55442
55443     /**
55444      * Returns the number of columns.
55445      * @return {Number}
55446      */
55447     getColumnCount : function(visibleOnly){
55448         if(visibleOnly === true){
55449             var c = 0;
55450             for(var i = 0, len = this.config.length; i < len; i++){
55451                 if(!this.isHidden(i)){
55452                     c++;
55453                 }
55454             }
55455             return c;
55456         }
55457         return this.config.length;
55458     },
55459
55460     /**
55461      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55462      * @param {Function} fn
55463      * @param {Object} scope (optional)
55464      * @return {Array} result
55465      */
55466     getColumnsBy : function(fn, scope){
55467         var r = [];
55468         for(var i = 0, len = this.config.length; i < len; i++){
55469             var c = this.config[i];
55470             if(fn.call(scope||this, c, i) === true){
55471                 r[r.length] = c;
55472             }
55473         }
55474         return r;
55475     },
55476
55477     /**
55478      * Returns true if the specified column is sortable.
55479      * @param {Number} col The column index
55480      * @return {Boolean}
55481      */
55482     isSortable : function(col){
55483         if(typeof this.config[col].sortable == "undefined"){
55484             return this.defaultSortable;
55485         }
55486         return this.config[col].sortable;
55487     },
55488
55489     /**
55490      * Returns the rendering (formatting) function defined for the column.
55491      * @param {Number} col The column index.
55492      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55493      */
55494     getRenderer : function(col){
55495         if(!this.config[col].renderer){
55496             return Roo.grid.ColumnModel.defaultRenderer;
55497         }
55498         return this.config[col].renderer;
55499     },
55500
55501     /**
55502      * Sets the rendering (formatting) function for a column.
55503      * @param {Number} col The column index
55504      * @param {Function} fn The function to use to process the cell's raw data
55505      * to return HTML markup for the grid view. The render function is called with
55506      * the following parameters:<ul>
55507      * <li>Data value.</li>
55508      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55509      * <li>css A CSS style string to apply to the table cell.</li>
55510      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55511      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55512      * <li>Row index</li>
55513      * <li>Column index</li>
55514      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55515      */
55516     setRenderer : function(col, fn){
55517         this.config[col].renderer = fn;
55518     },
55519
55520     /**
55521      * Returns the width for the specified column.
55522      * @param {Number} col The column index
55523      * @return {Number}
55524      */
55525     getColumnWidth : function(col){
55526         return this.config[col].width * 1 || this.defaultWidth;
55527     },
55528
55529     /**
55530      * Sets the width for a column.
55531      * @param {Number} col The column index
55532      * @param {Number} width The new width
55533      */
55534     setColumnWidth : function(col, width, suppressEvent){
55535         this.config[col].width = width;
55536         this.totalWidth = null;
55537         if(!suppressEvent){
55538              this.fireEvent("widthchange", this, col, width);
55539         }
55540     },
55541
55542     /**
55543      * Returns the total width of all columns.
55544      * @param {Boolean} includeHidden True to include hidden column widths
55545      * @return {Number}
55546      */
55547     getTotalWidth : function(includeHidden){
55548         if(!this.totalWidth){
55549             this.totalWidth = 0;
55550             for(var i = 0, len = this.config.length; i < len; i++){
55551                 if(includeHidden || !this.isHidden(i)){
55552                     this.totalWidth += this.getColumnWidth(i);
55553                 }
55554             }
55555         }
55556         return this.totalWidth;
55557     },
55558
55559     /**
55560      * Returns the header for the specified column.
55561      * @param {Number} col The column index
55562      * @return {String}
55563      */
55564     getColumnHeader : function(col){
55565         return this.config[col].header;
55566     },
55567
55568     /**
55569      * Sets the header for a column.
55570      * @param {Number} col The column index
55571      * @param {String} header The new header
55572      */
55573     setColumnHeader : function(col, header){
55574         this.config[col].header = header;
55575         this.fireEvent("headerchange", this, col, header);
55576     },
55577
55578     /**
55579      * Returns the tooltip for the specified column.
55580      * @param {Number} col The column index
55581      * @return {String}
55582      */
55583     getColumnTooltip : function(col){
55584             return this.config[col].tooltip;
55585     },
55586     /**
55587      * Sets the tooltip for a column.
55588      * @param {Number} col The column index
55589      * @param {String} tooltip The new tooltip
55590      */
55591     setColumnTooltip : function(col, tooltip){
55592             this.config[col].tooltip = tooltip;
55593     },
55594
55595     /**
55596      * Returns the dataIndex for the specified column.
55597      * @param {Number} col The column index
55598      * @return {Number}
55599      */
55600     getDataIndex : function(col){
55601         return this.config[col].dataIndex;
55602     },
55603
55604     /**
55605      * Sets the dataIndex for a column.
55606      * @param {Number} col The column index
55607      * @param {Number} dataIndex The new dataIndex
55608      */
55609     setDataIndex : function(col, dataIndex){
55610         this.config[col].dataIndex = dataIndex;
55611     },
55612
55613     
55614     
55615     /**
55616      * Returns true if the cell is editable.
55617      * @param {Number} colIndex The column index
55618      * @param {Number} rowIndex The row index
55619      * @return {Boolean}
55620      */
55621     isCellEditable : function(colIndex, rowIndex){
55622         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55623     },
55624
55625     /**
55626      * Returns the editor defined for the cell/column.
55627      * return false or null to disable editing.
55628      * @param {Number} colIndex The column index
55629      * @param {Number} rowIndex The row index
55630      * @return {Object}
55631      */
55632     getCellEditor : function(colIndex, rowIndex){
55633         return this.config[colIndex].editor;
55634     },
55635
55636     /**
55637      * Sets if a column is editable.
55638      * @param {Number} col The column index
55639      * @param {Boolean} editable True if the column is editable
55640      */
55641     setEditable : function(col, editable){
55642         this.config[col].editable = editable;
55643     },
55644
55645
55646     /**
55647      * Returns true if the column is hidden.
55648      * @param {Number} colIndex The column index
55649      * @return {Boolean}
55650      */
55651     isHidden : function(colIndex){
55652         return this.config[colIndex].hidden;
55653     },
55654
55655
55656     /**
55657      * Returns true if the column width cannot be changed
55658      */
55659     isFixed : function(colIndex){
55660         return this.config[colIndex].fixed;
55661     },
55662
55663     /**
55664      * Returns true if the column can be resized
55665      * @return {Boolean}
55666      */
55667     isResizable : function(colIndex){
55668         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55669     },
55670     /**
55671      * Sets if a column is hidden.
55672      * @param {Number} colIndex The column index
55673      * @param {Boolean} hidden True if the column is hidden
55674      */
55675     setHidden : function(colIndex, hidden){
55676         this.config[colIndex].hidden = hidden;
55677         this.totalWidth = null;
55678         this.fireEvent("hiddenchange", this, colIndex, hidden);
55679     },
55680
55681     /**
55682      * Sets the editor for a column.
55683      * @param {Number} col The column index
55684      * @param {Object} editor The editor object
55685      */
55686     setEditor : function(col, editor){
55687         this.config[col].editor = editor;
55688     }
55689 });
55690
55691 Roo.grid.ColumnModel.defaultRenderer = function(value){
55692         if(typeof value == "string" && value.length < 1){
55693             return "&#160;";
55694         }
55695         return value;
55696 };
55697
55698 // Alias for backwards compatibility
55699 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55700 /*
55701  * Based on:
55702  * Ext JS Library 1.1.1
55703  * Copyright(c) 2006-2007, Ext JS, LLC.
55704  *
55705  * Originally Released Under LGPL - original licence link has changed is not relivant.
55706  *
55707  * Fork - LGPL
55708  * <script type="text/javascript">
55709  */
55710
55711 /**
55712  * @class Roo.grid.AbstractSelectionModel
55713  * @extends Roo.util.Observable
55714  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55715  * implemented by descendant classes.  This class should not be directly instantiated.
55716  * @constructor
55717  */
55718 Roo.grid.AbstractSelectionModel = function(){
55719     this.locked = false;
55720     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55721 };
55722
55723 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55724     /** @ignore Called by the grid automatically. Do not call directly. */
55725     init : function(grid){
55726         this.grid = grid;
55727         this.initEvents();
55728     },
55729
55730     /**
55731      * Locks the selections.
55732      */
55733     lock : function(){
55734         this.locked = true;
55735     },
55736
55737     /**
55738      * Unlocks the selections.
55739      */
55740     unlock : function(){
55741         this.locked = false;
55742     },
55743
55744     /**
55745      * Returns true if the selections are locked.
55746      * @return {Boolean}
55747      */
55748     isLocked : function(){
55749         return this.locked;
55750     }
55751 });/*
55752  * Based on:
55753  * Ext JS Library 1.1.1
55754  * Copyright(c) 2006-2007, Ext JS, LLC.
55755  *
55756  * Originally Released Under LGPL - original licence link has changed is not relivant.
55757  *
55758  * Fork - LGPL
55759  * <script type="text/javascript">
55760  */
55761 /**
55762  * @extends Roo.grid.AbstractSelectionModel
55763  * @class Roo.grid.RowSelectionModel
55764  * The default SelectionModel used by {@link Roo.grid.Grid}.
55765  * It supports multiple selections and keyboard selection/navigation. 
55766  * @constructor
55767  * @param {Object} config
55768  */
55769 Roo.grid.RowSelectionModel = function(config){
55770     Roo.apply(this, config);
55771     this.selections = new Roo.util.MixedCollection(false, function(o){
55772         return o.id;
55773     });
55774
55775     this.last = false;
55776     this.lastActive = false;
55777
55778     this.addEvents({
55779         /**
55780              * @event selectionchange
55781              * Fires when the selection changes
55782              * @param {SelectionModel} this
55783              */
55784             "selectionchange" : true,
55785         /**
55786              * @event afterselectionchange
55787              * Fires after the selection changes (eg. by key press or clicking)
55788              * @param {SelectionModel} this
55789              */
55790             "afterselectionchange" : true,
55791         /**
55792              * @event beforerowselect
55793              * Fires when a row is selected being selected, return false to cancel.
55794              * @param {SelectionModel} this
55795              * @param {Number} rowIndex The selected index
55796              * @param {Boolean} keepExisting False if other selections will be cleared
55797              */
55798             "beforerowselect" : true,
55799         /**
55800              * @event rowselect
55801              * Fires when a row is selected.
55802              * @param {SelectionModel} this
55803              * @param {Number} rowIndex The selected index
55804              * @param {Roo.data.Record} r The record
55805              */
55806             "rowselect" : true,
55807         /**
55808              * @event rowdeselect
55809              * Fires when a row is deselected.
55810              * @param {SelectionModel} this
55811              * @param {Number} rowIndex The selected index
55812              */
55813         "rowdeselect" : true
55814     });
55815     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55816     this.locked = false;
55817 };
55818
55819 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55820     /**
55821      * @cfg {Boolean} singleSelect
55822      * True to allow selection of only one row at a time (defaults to false)
55823      */
55824     singleSelect : false,
55825
55826     // private
55827     initEvents : function(){
55828
55829         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55830             this.grid.on("mousedown", this.handleMouseDown, this);
55831         }else{ // allow click to work like normal
55832             this.grid.on("rowclick", this.handleDragableRowClick, this);
55833         }
55834
55835         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55836             "up" : function(e){
55837                 if(!e.shiftKey){
55838                     this.selectPrevious(e.shiftKey);
55839                 }else if(this.last !== false && this.lastActive !== false){
55840                     var last = this.last;
55841                     this.selectRange(this.last,  this.lastActive-1);
55842                     this.grid.getView().focusRow(this.lastActive);
55843                     if(last !== false){
55844                         this.last = last;
55845                     }
55846                 }else{
55847                     this.selectFirstRow();
55848                 }
55849                 this.fireEvent("afterselectionchange", this);
55850             },
55851             "down" : function(e){
55852                 if(!e.shiftKey){
55853                     this.selectNext(e.shiftKey);
55854                 }else if(this.last !== false && this.lastActive !== false){
55855                     var last = this.last;
55856                     this.selectRange(this.last,  this.lastActive+1);
55857                     this.grid.getView().focusRow(this.lastActive);
55858                     if(last !== false){
55859                         this.last = last;
55860                     }
55861                 }else{
55862                     this.selectFirstRow();
55863                 }
55864                 this.fireEvent("afterselectionchange", this);
55865             },
55866             scope: this
55867         });
55868
55869         var view = this.grid.view;
55870         view.on("refresh", this.onRefresh, this);
55871         view.on("rowupdated", this.onRowUpdated, this);
55872         view.on("rowremoved", this.onRemove, this);
55873     },
55874
55875     // private
55876     onRefresh : function(){
55877         var ds = this.grid.dataSource, i, v = this.grid.view;
55878         var s = this.selections;
55879         s.each(function(r){
55880             if((i = ds.indexOfId(r.id)) != -1){
55881                 v.onRowSelect(i);
55882             }else{
55883                 s.remove(r);
55884             }
55885         });
55886     },
55887
55888     // private
55889     onRemove : function(v, index, r){
55890         this.selections.remove(r);
55891     },
55892
55893     // private
55894     onRowUpdated : function(v, index, r){
55895         if(this.isSelected(r)){
55896             v.onRowSelect(index);
55897         }
55898     },
55899
55900     /**
55901      * Select records.
55902      * @param {Array} records The records to select
55903      * @param {Boolean} keepExisting (optional) True to keep existing selections
55904      */
55905     selectRecords : function(records, keepExisting){
55906         if(!keepExisting){
55907             this.clearSelections();
55908         }
55909         var ds = this.grid.dataSource;
55910         for(var i = 0, len = records.length; i < len; i++){
55911             this.selectRow(ds.indexOf(records[i]), true);
55912         }
55913     },
55914
55915     /**
55916      * Gets the number of selected rows.
55917      * @return {Number}
55918      */
55919     getCount : function(){
55920         return this.selections.length;
55921     },
55922
55923     /**
55924      * Selects the first row in the grid.
55925      */
55926     selectFirstRow : function(){
55927         this.selectRow(0);
55928     },
55929
55930     /**
55931      * Select the last row.
55932      * @param {Boolean} keepExisting (optional) True to keep existing selections
55933      */
55934     selectLastRow : function(keepExisting){
55935         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55936     },
55937
55938     /**
55939      * Selects the row immediately following the last selected row.
55940      * @param {Boolean} keepExisting (optional) True to keep existing selections
55941      */
55942     selectNext : function(keepExisting){
55943         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55944             this.selectRow(this.last+1, keepExisting);
55945             this.grid.getView().focusRow(this.last);
55946         }
55947     },
55948
55949     /**
55950      * Selects the row that precedes the last selected row.
55951      * @param {Boolean} keepExisting (optional) True to keep existing selections
55952      */
55953     selectPrevious : function(keepExisting){
55954         if(this.last){
55955             this.selectRow(this.last-1, keepExisting);
55956             this.grid.getView().focusRow(this.last);
55957         }
55958     },
55959
55960     /**
55961      * Returns the selected records
55962      * @return {Array} Array of selected records
55963      */
55964     getSelections : function(){
55965         return [].concat(this.selections.items);
55966     },
55967
55968     /**
55969      * Returns the first selected record.
55970      * @return {Record}
55971      */
55972     getSelected : function(){
55973         return this.selections.itemAt(0);
55974     },
55975
55976
55977     /**
55978      * Clears all selections.
55979      */
55980     clearSelections : function(fast){
55981         if(this.locked) return;
55982         if(fast !== true){
55983             var ds = this.grid.dataSource;
55984             var s = this.selections;
55985             s.each(function(r){
55986                 this.deselectRow(ds.indexOfId(r.id));
55987             }, this);
55988             s.clear();
55989         }else{
55990             this.selections.clear();
55991         }
55992         this.last = false;
55993     },
55994
55995
55996     /**
55997      * Selects all rows.
55998      */
55999     selectAll : function(){
56000         if(this.locked) return;
56001         this.selections.clear();
56002         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56003             this.selectRow(i, true);
56004         }
56005     },
56006
56007     /**
56008      * Returns True if there is a selection.
56009      * @return {Boolean}
56010      */
56011     hasSelection : function(){
56012         return this.selections.length > 0;
56013     },
56014
56015     /**
56016      * Returns True if the specified row is selected.
56017      * @param {Number/Record} record The record or index of the record to check
56018      * @return {Boolean}
56019      */
56020     isSelected : function(index){
56021         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56022         return (r && this.selections.key(r.id) ? true : false);
56023     },
56024
56025     /**
56026      * Returns True if the specified record id is selected.
56027      * @param {String} id The id of record to check
56028      * @return {Boolean}
56029      */
56030     isIdSelected : function(id){
56031         return (this.selections.key(id) ? true : false);
56032     },
56033
56034     // private
56035     handleMouseDown : function(e, t){
56036         var view = this.grid.getView(), rowIndex;
56037         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56038             return;
56039         };
56040         if(e.shiftKey && this.last !== false){
56041             var last = this.last;
56042             this.selectRange(last, rowIndex, e.ctrlKey);
56043             this.last = last; // reset the last
56044             view.focusRow(rowIndex);
56045         }else{
56046             var isSelected = this.isSelected(rowIndex);
56047             if(e.button !== 0 && isSelected){
56048                 view.focusRow(rowIndex);
56049             }else if(e.ctrlKey && isSelected){
56050                 this.deselectRow(rowIndex);
56051             }else if(!isSelected){
56052                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56053                 view.focusRow(rowIndex);
56054             }
56055         }
56056         this.fireEvent("afterselectionchange", this);
56057     },
56058     // private
56059     handleDragableRowClick :  function(grid, rowIndex, e) 
56060     {
56061         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56062             this.selectRow(rowIndex, false);
56063             grid.view.focusRow(rowIndex);
56064              this.fireEvent("afterselectionchange", this);
56065         }
56066     },
56067     
56068     /**
56069      * Selects multiple rows.
56070      * @param {Array} rows Array of the indexes of the row to select
56071      * @param {Boolean} keepExisting (optional) True to keep existing selections
56072      */
56073     selectRows : function(rows, keepExisting){
56074         if(!keepExisting){
56075             this.clearSelections();
56076         }
56077         for(var i = 0, len = rows.length; i < len; i++){
56078             this.selectRow(rows[i], true);
56079         }
56080     },
56081
56082     /**
56083      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56084      * @param {Number} startRow The index of the first row in the range
56085      * @param {Number} endRow The index of the last row in the range
56086      * @param {Boolean} keepExisting (optional) True to retain existing selections
56087      */
56088     selectRange : function(startRow, endRow, keepExisting){
56089         if(this.locked) return;
56090         if(!keepExisting){
56091             this.clearSelections();
56092         }
56093         if(startRow <= endRow){
56094             for(var i = startRow; i <= endRow; i++){
56095                 this.selectRow(i, true);
56096             }
56097         }else{
56098             for(var i = startRow; i >= endRow; i--){
56099                 this.selectRow(i, true);
56100             }
56101         }
56102     },
56103
56104     /**
56105      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56106      * @param {Number} startRow The index of the first row in the range
56107      * @param {Number} endRow The index of the last row in the range
56108      */
56109     deselectRange : function(startRow, endRow, preventViewNotify){
56110         if(this.locked) return;
56111         for(var i = startRow; i <= endRow; i++){
56112             this.deselectRow(i, preventViewNotify);
56113         }
56114     },
56115
56116     /**
56117      * Selects a row.
56118      * @param {Number} row The index of the row to select
56119      * @param {Boolean} keepExisting (optional) True to keep existing selections
56120      */
56121     selectRow : function(index, keepExisting, preventViewNotify){
56122         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56123         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56124             if(!keepExisting || this.singleSelect){
56125                 this.clearSelections();
56126             }
56127             var r = this.grid.dataSource.getAt(index);
56128             this.selections.add(r);
56129             this.last = this.lastActive = index;
56130             if(!preventViewNotify){
56131                 this.grid.getView().onRowSelect(index);
56132             }
56133             this.fireEvent("rowselect", this, index, r);
56134             this.fireEvent("selectionchange", this);
56135         }
56136     },
56137
56138     /**
56139      * Deselects a row.
56140      * @param {Number} row The index of the row to deselect
56141      */
56142     deselectRow : function(index, preventViewNotify){
56143         if(this.locked) return;
56144         if(this.last == index){
56145             this.last = false;
56146         }
56147         if(this.lastActive == index){
56148             this.lastActive = false;
56149         }
56150         var r = this.grid.dataSource.getAt(index);
56151         this.selections.remove(r);
56152         if(!preventViewNotify){
56153             this.grid.getView().onRowDeselect(index);
56154         }
56155         this.fireEvent("rowdeselect", this, index);
56156         this.fireEvent("selectionchange", this);
56157     },
56158
56159     // private
56160     restoreLast : function(){
56161         if(this._last){
56162             this.last = this._last;
56163         }
56164     },
56165
56166     // private
56167     acceptsNav : function(row, col, cm){
56168         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56169     },
56170
56171     // private
56172     onEditorKey : function(field, e){
56173         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56174         if(k == e.TAB){
56175             e.stopEvent();
56176             ed.completeEdit();
56177             if(e.shiftKey){
56178                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56179             }else{
56180                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56181             }
56182         }else if(k == e.ENTER && !e.ctrlKey){
56183             e.stopEvent();
56184             ed.completeEdit();
56185             if(e.shiftKey){
56186                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56187             }else{
56188                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56189             }
56190         }else if(k == e.ESC){
56191             ed.cancelEdit();
56192         }
56193         if(newCell){
56194             g.startEditing(newCell[0], newCell[1]);
56195         }
56196     }
56197 });/*
56198  * Based on:
56199  * Ext JS Library 1.1.1
56200  * Copyright(c) 2006-2007, Ext JS, LLC.
56201  *
56202  * Originally Released Under LGPL - original licence link has changed is not relivant.
56203  *
56204  * Fork - LGPL
56205  * <script type="text/javascript">
56206  */
56207 /**
56208  * @class Roo.grid.CellSelectionModel
56209  * @extends Roo.grid.AbstractSelectionModel
56210  * This class provides the basic implementation for cell selection in a grid.
56211  * @constructor
56212  * @param {Object} config The object containing the configuration of this model.
56213  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56214  */
56215 Roo.grid.CellSelectionModel = function(config){
56216     Roo.apply(this, config);
56217
56218     this.selection = null;
56219
56220     this.addEvents({
56221         /**
56222              * @event beforerowselect
56223              * Fires before a cell is selected.
56224              * @param {SelectionModel} this
56225              * @param {Number} rowIndex The selected row index
56226              * @param {Number} colIndex The selected cell index
56227              */
56228             "beforecellselect" : true,
56229         /**
56230              * @event cellselect
56231              * Fires when a cell is selected.
56232              * @param {SelectionModel} this
56233              * @param {Number} rowIndex The selected row index
56234              * @param {Number} colIndex The selected cell index
56235              */
56236             "cellselect" : true,
56237         /**
56238              * @event selectionchange
56239              * Fires when the active selection changes.
56240              * @param {SelectionModel} this
56241              * @param {Object} selection null for no selection or an object (o) with two properties
56242                 <ul>
56243                 <li>o.record: the record object for the row the selection is in</li>
56244                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56245                 </ul>
56246              */
56247             "selectionchange" : true,
56248         /**
56249              * @event tabend
56250              * Fires when the tab (or enter) was pressed on the last editable cell
56251              * You can use this to trigger add new row.
56252              * @param {SelectionModel} this
56253              */
56254             "tabend" : true,
56255          /**
56256              * @event beforeeditnext
56257              * Fires before the next editable sell is made active
56258              * You can use this to skip to another cell or fire the tabend
56259              *    if you set cell to false
56260              * @param {Object} eventdata object : { cell : [ row, col ] } 
56261              */
56262             "beforeeditnext" : true
56263     });
56264     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56265 };
56266
56267 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56268     
56269     enter_is_tab: false,
56270
56271     /** @ignore */
56272     initEvents : function(){
56273         this.grid.on("mousedown", this.handleMouseDown, this);
56274         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56275         var view = this.grid.view;
56276         view.on("refresh", this.onViewChange, this);
56277         view.on("rowupdated", this.onRowUpdated, this);
56278         view.on("beforerowremoved", this.clearSelections, this);
56279         view.on("beforerowsinserted", this.clearSelections, this);
56280         if(this.grid.isEditor){
56281             this.grid.on("beforeedit", this.beforeEdit,  this);
56282         }
56283     },
56284
56285         //private
56286     beforeEdit : function(e){
56287         this.select(e.row, e.column, false, true, e.record);
56288     },
56289
56290         //private
56291     onRowUpdated : function(v, index, r){
56292         if(this.selection && this.selection.record == r){
56293             v.onCellSelect(index, this.selection.cell[1]);
56294         }
56295     },
56296
56297         //private
56298     onViewChange : function(){
56299         this.clearSelections(true);
56300     },
56301
56302         /**
56303          * Returns the currently selected cell,.
56304          * @return {Array} The selected cell (row, column) or null if none selected.
56305          */
56306     getSelectedCell : function(){
56307         return this.selection ? this.selection.cell : null;
56308     },
56309
56310     /**
56311      * Clears all selections.
56312      * @param {Boolean} true to prevent the gridview from being notified about the change.
56313      */
56314     clearSelections : function(preventNotify){
56315         var s = this.selection;
56316         if(s){
56317             if(preventNotify !== true){
56318                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56319             }
56320             this.selection = null;
56321             this.fireEvent("selectionchange", this, null);
56322         }
56323     },
56324
56325     /**
56326      * Returns true if there is a selection.
56327      * @return {Boolean}
56328      */
56329     hasSelection : function(){
56330         return this.selection ? true : false;
56331     },
56332
56333     /** @ignore */
56334     handleMouseDown : function(e, t){
56335         var v = this.grid.getView();
56336         if(this.isLocked()){
56337             return;
56338         };
56339         var row = v.findRowIndex(t);
56340         var cell = v.findCellIndex(t);
56341         if(row !== false && cell !== false){
56342             this.select(row, cell);
56343         }
56344     },
56345
56346     /**
56347      * Selects a cell.
56348      * @param {Number} rowIndex
56349      * @param {Number} collIndex
56350      */
56351     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56352         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56353             this.clearSelections();
56354             r = r || this.grid.dataSource.getAt(rowIndex);
56355             this.selection = {
56356                 record : r,
56357                 cell : [rowIndex, colIndex]
56358             };
56359             if(!preventViewNotify){
56360                 var v = this.grid.getView();
56361                 v.onCellSelect(rowIndex, colIndex);
56362                 if(preventFocus !== true){
56363                     v.focusCell(rowIndex, colIndex);
56364                 }
56365             }
56366             this.fireEvent("cellselect", this, rowIndex, colIndex);
56367             this.fireEvent("selectionchange", this, this.selection);
56368         }
56369     },
56370
56371         //private
56372     isSelectable : function(rowIndex, colIndex, cm){
56373         return !cm.isHidden(colIndex);
56374     },
56375
56376     /** @ignore */
56377     handleKeyDown : function(e){
56378         //Roo.log('Cell Sel Model handleKeyDown');
56379         if(!e.isNavKeyPress()){
56380             return;
56381         }
56382         var g = this.grid, s = this.selection;
56383         if(!s){
56384             e.stopEvent();
56385             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56386             if(cell){
56387                 this.select(cell[0], cell[1]);
56388             }
56389             return;
56390         }
56391         var sm = this;
56392         var walk = function(row, col, step){
56393             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56394         };
56395         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56396         var newCell;
56397
56398       
56399
56400         switch(k){
56401             case e.TAB:
56402                 // handled by onEditorKey
56403                 if (g.isEditor && g.editing) {
56404                     return;
56405                 }
56406                 if(e.shiftKey) {
56407                     newCell = walk(r, c-1, -1);
56408                 } else {
56409                     newCell = walk(r, c+1, 1);
56410                 }
56411                 break;
56412             
56413             case e.DOWN:
56414                newCell = walk(r+1, c, 1);
56415                 break;
56416             
56417             case e.UP:
56418                 newCell = walk(r-1, c, -1);
56419                 break;
56420             
56421             case e.RIGHT:
56422                 newCell = walk(r, c+1, 1);
56423                 break;
56424             
56425             case e.LEFT:
56426                 newCell = walk(r, c-1, -1);
56427                 break;
56428             
56429             case e.ENTER:
56430                 
56431                 if(g.isEditor && !g.editing){
56432                    g.startEditing(r, c);
56433                    e.stopEvent();
56434                    return;
56435                 }
56436                 
56437                 
56438              break;
56439         };
56440         if(newCell){
56441             this.select(newCell[0], newCell[1]);
56442             e.stopEvent();
56443             
56444         }
56445     },
56446
56447     acceptsNav : function(row, col, cm){
56448         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56449     },
56450     /**
56451      * Selects a cell.
56452      * @param {Number} field (not used) - as it's normally used as a listener
56453      * @param {Number} e - event - fake it by using
56454      *
56455      * var e = Roo.EventObjectImpl.prototype;
56456      * e.keyCode = e.TAB
56457      *
56458      * 
56459      */
56460     onEditorKey : function(field, e){
56461         
56462         var k = e.getKey(),
56463             newCell,
56464             g = this.grid,
56465             ed = g.activeEditor,
56466             forward = false;
56467         ///Roo.log('onEditorKey' + k);
56468         
56469         
56470         if (this.enter_is_tab && k == e.ENTER) {
56471             k = e.TAB;
56472         }
56473         
56474         if(k == e.TAB){
56475             if(e.shiftKey){
56476                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56477             }else{
56478                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56479                 forward = true;
56480             }
56481             
56482             e.stopEvent();
56483             
56484         } else if(k == e.ENTER &&  !e.ctrlKey){
56485             ed.completeEdit();
56486             e.stopEvent();
56487             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56488         
56489                 } else if(k == e.ESC){
56490             ed.cancelEdit();
56491         }
56492                 
56493         if (newCell) {
56494             var ecall = { cell : newCell, forward : forward };
56495             this.fireEvent('beforeeditnext', ecall );
56496             newCell = ecall.cell;
56497                         forward = ecall.forward;
56498         }
56499                 
56500         if(newCell){
56501             //Roo.log('next cell after edit');
56502             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56503         } else if (forward) {
56504             // tabbed past last
56505             this.fireEvent.defer(100, this, ['tabend',this]);
56506         }
56507     }
56508 });/*
56509  * Based on:
56510  * Ext JS Library 1.1.1
56511  * Copyright(c) 2006-2007, Ext JS, LLC.
56512  *
56513  * Originally Released Under LGPL - original licence link has changed is not relivant.
56514  *
56515  * Fork - LGPL
56516  * <script type="text/javascript">
56517  */
56518  
56519 /**
56520  * @class Roo.grid.EditorGrid
56521  * @extends Roo.grid.Grid
56522  * Class for creating and editable grid.
56523  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56524  * The container MUST have some type of size defined for the grid to fill. The container will be 
56525  * automatically set to position relative if it isn't already.
56526  * @param {Object} dataSource The data model to bind to
56527  * @param {Object} colModel The column model with info about this grid's columns
56528  */
56529 Roo.grid.EditorGrid = function(container, config){
56530     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56531     this.getGridEl().addClass("xedit-grid");
56532
56533     if(!this.selModel){
56534         this.selModel = new Roo.grid.CellSelectionModel();
56535     }
56536
56537     this.activeEditor = null;
56538
56539         this.addEvents({
56540             /**
56541              * @event beforeedit
56542              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56543              * <ul style="padding:5px;padding-left:16px;">
56544              * <li>grid - This grid</li>
56545              * <li>record - The record being edited</li>
56546              * <li>field - The field name being edited</li>
56547              * <li>value - The value for the field being edited.</li>
56548              * <li>row - The grid row index</li>
56549              * <li>column - The grid column index</li>
56550              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56551              * </ul>
56552              * @param {Object} e An edit event (see above for description)
56553              */
56554             "beforeedit" : true,
56555             /**
56556              * @event afteredit
56557              * Fires after a cell is edited. <br />
56558              * <ul style="padding:5px;padding-left:16px;">
56559              * <li>grid - This grid</li>
56560              * <li>record - The record being edited</li>
56561              * <li>field - The field name being edited</li>
56562              * <li>value - The value being set</li>
56563              * <li>originalValue - The original value for the field, before the edit.</li>
56564              * <li>row - The grid row index</li>
56565              * <li>column - The grid column index</li>
56566              * </ul>
56567              * @param {Object} e An edit event (see above for description)
56568              */
56569             "afteredit" : true,
56570             /**
56571              * @event validateedit
56572              * Fires after a cell is edited, but before the value is set in the record. 
56573          * You can use this to modify the value being set in the field, Return false
56574              * to cancel the change. The edit event object has the following properties <br />
56575              * <ul style="padding:5px;padding-left:16px;">
56576          * <li>editor - This editor</li>
56577              * <li>grid - This grid</li>
56578              * <li>record - The record being edited</li>
56579              * <li>field - The field name being edited</li>
56580              * <li>value - The value being set</li>
56581              * <li>originalValue - The original value for the field, before the edit.</li>
56582              * <li>row - The grid row index</li>
56583              * <li>column - The grid column index</li>
56584              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56585              * </ul>
56586              * @param {Object} e An edit event (see above for description)
56587              */
56588             "validateedit" : true
56589         });
56590     this.on("bodyscroll", this.stopEditing,  this);
56591     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56592 };
56593
56594 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56595     /**
56596      * @cfg {Number} clicksToEdit
56597      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56598      */
56599     clicksToEdit: 2,
56600
56601     // private
56602     isEditor : true,
56603     // private
56604     trackMouseOver: false, // causes very odd FF errors
56605
56606     onCellDblClick : function(g, row, col){
56607         this.startEditing(row, col);
56608     },
56609
56610     onEditComplete : function(ed, value, startValue){
56611         this.editing = false;
56612         this.activeEditor = null;
56613         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56614         var r = ed.record;
56615         var field = this.colModel.getDataIndex(ed.col);
56616         var e = {
56617             grid: this,
56618             record: r,
56619             field: field,
56620             originalValue: startValue,
56621             value: value,
56622             row: ed.row,
56623             column: ed.col,
56624             cancel:false,
56625             editor: ed
56626         };
56627         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56628         cell.show();
56629           
56630         if(String(value) !== String(startValue)){
56631             
56632             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56633                 r.set(field, e.value);
56634                 // if we are dealing with a combo box..
56635                 // then we also set the 'name' colum to be the displayField
56636                 if (ed.field.displayField && ed.field.name) {
56637                     r.set(ed.field.name, ed.field.el.dom.value);
56638                 }
56639                 
56640                 delete e.cancel; //?? why!!!
56641                 this.fireEvent("afteredit", e);
56642             }
56643         } else {
56644             this.fireEvent("afteredit", e); // always fire it!
56645         }
56646         this.view.focusCell(ed.row, ed.col);
56647     },
56648
56649     /**
56650      * Starts editing the specified for the specified row/column
56651      * @param {Number} rowIndex
56652      * @param {Number} colIndex
56653      */
56654     startEditing : function(row, col){
56655         this.stopEditing();
56656         if(this.colModel.isCellEditable(col, row)){
56657             this.view.ensureVisible(row, col, true);
56658           
56659             var r = this.dataSource.getAt(row);
56660             var field = this.colModel.getDataIndex(col);
56661             var cell = Roo.get(this.view.getCell(row,col));
56662             var e = {
56663                 grid: this,
56664                 record: r,
56665                 field: field,
56666                 value: r.data[field],
56667                 row: row,
56668                 column: col,
56669                 cancel:false 
56670             };
56671             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56672                 this.editing = true;
56673                 var ed = this.colModel.getCellEditor(col, row);
56674                 
56675                 if (!ed) {
56676                     return;
56677                 }
56678                 if(!ed.rendered){
56679                     ed.render(ed.parentEl || document.body);
56680                 }
56681                 ed.field.reset();
56682                
56683                 cell.hide();
56684                 
56685                 (function(){ // complex but required for focus issues in safari, ie and opera
56686                     ed.row = row;
56687                     ed.col = col;
56688                     ed.record = r;
56689                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56690                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56691                     this.activeEditor = ed;
56692                     var v = r.data[field];
56693                     ed.startEdit(this.view.getCell(row, col), v);
56694                     // combo's with 'displayField and name set
56695                     if (ed.field.displayField && ed.field.name) {
56696                         ed.field.el.dom.value = r.data[ed.field.name];
56697                     }
56698                     
56699                     
56700                 }).defer(50, this);
56701             }
56702         }
56703     },
56704         
56705     /**
56706      * Stops any active editing
56707      */
56708     stopEditing : function(){
56709         if(this.activeEditor){
56710             this.activeEditor.completeEdit();
56711         }
56712         this.activeEditor = null;
56713     },
56714         
56715          /**
56716      * Called to get grid's drag proxy text, by default returns this.ddText.
56717      * @return {String}
56718      */
56719     getDragDropText : function(){
56720         var count = this.selModel.getSelectedCell() ? 1 : 0;
56721         return String.format(this.ddText, count, count == 1 ? '' : 's');
56722     }
56723         
56724 });/*
56725  * Based on:
56726  * Ext JS Library 1.1.1
56727  * Copyright(c) 2006-2007, Ext JS, LLC.
56728  *
56729  * Originally Released Under LGPL - original licence link has changed is not relivant.
56730  *
56731  * Fork - LGPL
56732  * <script type="text/javascript">
56733  */
56734
56735 // private - not really -- you end up using it !
56736 // This is a support class used internally by the Grid components
56737
56738 /**
56739  * @class Roo.grid.GridEditor
56740  * @extends Roo.Editor
56741  * Class for creating and editable grid elements.
56742  * @param {Object} config any settings (must include field)
56743  */
56744 Roo.grid.GridEditor = function(field, config){
56745     if (!config && field.field) {
56746         config = field;
56747         field = Roo.factory(config.field, Roo.form);
56748     }
56749     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56750     field.monitorTab = false;
56751 };
56752
56753 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56754     
56755     /**
56756      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56757      */
56758     
56759     alignment: "tl-tl",
56760     autoSize: "width",
56761     hideEl : false,
56762     cls: "x-small-editor x-grid-editor",
56763     shim:false,
56764     shadow:"frame"
56765 });/*
56766  * Based on:
56767  * Ext JS Library 1.1.1
56768  * Copyright(c) 2006-2007, Ext JS, LLC.
56769  *
56770  * Originally Released Under LGPL - original licence link has changed is not relivant.
56771  *
56772  * Fork - LGPL
56773  * <script type="text/javascript">
56774  */
56775   
56776
56777   
56778 Roo.grid.PropertyRecord = Roo.data.Record.create([
56779     {name:'name',type:'string'},  'value'
56780 ]);
56781
56782
56783 Roo.grid.PropertyStore = function(grid, source){
56784     this.grid = grid;
56785     this.store = new Roo.data.Store({
56786         recordType : Roo.grid.PropertyRecord
56787     });
56788     this.store.on('update', this.onUpdate,  this);
56789     if(source){
56790         this.setSource(source);
56791     }
56792     Roo.grid.PropertyStore.superclass.constructor.call(this);
56793 };
56794
56795
56796
56797 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56798     setSource : function(o){
56799         this.source = o;
56800         this.store.removeAll();
56801         var data = [];
56802         for(var k in o){
56803             if(this.isEditableValue(o[k])){
56804                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56805             }
56806         }
56807         this.store.loadRecords({records: data}, {}, true);
56808     },
56809
56810     onUpdate : function(ds, record, type){
56811         if(type == Roo.data.Record.EDIT){
56812             var v = record.data['value'];
56813             var oldValue = record.modified['value'];
56814             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56815                 this.source[record.id] = v;
56816                 record.commit();
56817                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56818             }else{
56819                 record.reject();
56820             }
56821         }
56822     },
56823
56824     getProperty : function(row){
56825        return this.store.getAt(row);
56826     },
56827
56828     isEditableValue: function(val){
56829         if(val && val instanceof Date){
56830             return true;
56831         }else if(typeof val == 'object' || typeof val == 'function'){
56832             return false;
56833         }
56834         return true;
56835     },
56836
56837     setValue : function(prop, value){
56838         this.source[prop] = value;
56839         this.store.getById(prop).set('value', value);
56840     },
56841
56842     getSource : function(){
56843         return this.source;
56844     }
56845 });
56846
56847 Roo.grid.PropertyColumnModel = function(grid, store){
56848     this.grid = grid;
56849     var g = Roo.grid;
56850     g.PropertyColumnModel.superclass.constructor.call(this, [
56851         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56852         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56853     ]);
56854     this.store = store;
56855     this.bselect = Roo.DomHelper.append(document.body, {
56856         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56857             {tag: 'option', value: 'true', html: 'true'},
56858             {tag: 'option', value: 'false', html: 'false'}
56859         ]
56860     });
56861     Roo.id(this.bselect);
56862     var f = Roo.form;
56863     this.editors = {
56864         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56865         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56866         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56867         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56868         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56869     };
56870     this.renderCellDelegate = this.renderCell.createDelegate(this);
56871     this.renderPropDelegate = this.renderProp.createDelegate(this);
56872 };
56873
56874 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56875     
56876     
56877     nameText : 'Name',
56878     valueText : 'Value',
56879     
56880     dateFormat : 'm/j/Y',
56881     
56882     
56883     renderDate : function(dateVal){
56884         return dateVal.dateFormat(this.dateFormat);
56885     },
56886
56887     renderBool : function(bVal){
56888         return bVal ? 'true' : 'false';
56889     },
56890
56891     isCellEditable : function(colIndex, rowIndex){
56892         return colIndex == 1;
56893     },
56894
56895     getRenderer : function(col){
56896         return col == 1 ?
56897             this.renderCellDelegate : this.renderPropDelegate;
56898     },
56899
56900     renderProp : function(v){
56901         return this.getPropertyName(v);
56902     },
56903
56904     renderCell : function(val){
56905         var rv = val;
56906         if(val instanceof Date){
56907             rv = this.renderDate(val);
56908         }else if(typeof val == 'boolean'){
56909             rv = this.renderBool(val);
56910         }
56911         return Roo.util.Format.htmlEncode(rv);
56912     },
56913
56914     getPropertyName : function(name){
56915         var pn = this.grid.propertyNames;
56916         return pn && pn[name] ? pn[name] : name;
56917     },
56918
56919     getCellEditor : function(colIndex, rowIndex){
56920         var p = this.store.getProperty(rowIndex);
56921         var n = p.data['name'], val = p.data['value'];
56922         
56923         if(typeof(this.grid.customEditors[n]) == 'string'){
56924             return this.editors[this.grid.customEditors[n]];
56925         }
56926         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56927             return this.grid.customEditors[n];
56928         }
56929         if(val instanceof Date){
56930             return this.editors['date'];
56931         }else if(typeof val == 'number'){
56932             return this.editors['number'];
56933         }else if(typeof val == 'boolean'){
56934             return this.editors['boolean'];
56935         }else{
56936             return this.editors['string'];
56937         }
56938     }
56939 });
56940
56941 /**
56942  * @class Roo.grid.PropertyGrid
56943  * @extends Roo.grid.EditorGrid
56944  * This class represents the  interface of a component based property grid control.
56945  * <br><br>Usage:<pre><code>
56946  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56947       
56948  });
56949  // set any options
56950  grid.render();
56951  * </code></pre>
56952   
56953  * @constructor
56954  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56955  * The container MUST have some type of size defined for the grid to fill. The container will be
56956  * automatically set to position relative if it isn't already.
56957  * @param {Object} config A config object that sets properties on this grid.
56958  */
56959 Roo.grid.PropertyGrid = function(container, config){
56960     config = config || {};
56961     var store = new Roo.grid.PropertyStore(this);
56962     this.store = store;
56963     var cm = new Roo.grid.PropertyColumnModel(this, store);
56964     store.store.sort('name', 'ASC');
56965     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56966         ds: store.store,
56967         cm: cm,
56968         enableColLock:false,
56969         enableColumnMove:false,
56970         stripeRows:false,
56971         trackMouseOver: false,
56972         clicksToEdit:1
56973     }, config));
56974     this.getGridEl().addClass('x-props-grid');
56975     this.lastEditRow = null;
56976     this.on('columnresize', this.onColumnResize, this);
56977     this.addEvents({
56978          /**
56979              * @event beforepropertychange
56980              * Fires before a property changes (return false to stop?)
56981              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56982              * @param {String} id Record Id
56983              * @param {String} newval New Value
56984          * @param {String} oldval Old Value
56985              */
56986         "beforepropertychange": true,
56987         /**
56988              * @event propertychange
56989              * Fires after a property changes
56990              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56991              * @param {String} id Record Id
56992              * @param {String} newval New Value
56993          * @param {String} oldval Old Value
56994              */
56995         "propertychange": true
56996     });
56997     this.customEditors = this.customEditors || {};
56998 };
56999 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57000     
57001      /**
57002      * @cfg {Object} customEditors map of colnames=> custom editors.
57003      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57004      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57005      * false disables editing of the field.
57006          */
57007     
57008       /**
57009      * @cfg {Object} propertyNames map of property Names to their displayed value
57010          */
57011     
57012     render : function(){
57013         Roo.grid.PropertyGrid.superclass.render.call(this);
57014         this.autoSize.defer(100, this);
57015     },
57016
57017     autoSize : function(){
57018         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57019         if(this.view){
57020             this.view.fitColumns();
57021         }
57022     },
57023
57024     onColumnResize : function(){
57025         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57026         this.autoSize();
57027     },
57028     /**
57029      * Sets the data for the Grid
57030      * accepts a Key => Value object of all the elements avaiable.
57031      * @param {Object} data  to appear in grid.
57032      */
57033     setSource : function(source){
57034         this.store.setSource(source);
57035         //this.autoSize();
57036     },
57037     /**
57038      * Gets all the data from the grid.
57039      * @return {Object} data  data stored in grid
57040      */
57041     getSource : function(){
57042         return this.store.getSource();
57043     }
57044 });/*
57045   
57046  * Licence LGPL
57047  
57048  */
57049  
57050 /**
57051  * @class Roo.grid.Calendar
57052  * @extends Roo.util.Grid
57053  * This class extends the Grid to provide a calendar widget
57054  * <br><br>Usage:<pre><code>
57055  var grid = new Roo.grid.Calendar("my-container-id", {
57056      ds: myDataStore,
57057      cm: myColModel,
57058      selModel: mySelectionModel,
57059      autoSizeColumns: true,
57060      monitorWindowResize: false,
57061      trackMouseOver: true
57062      eventstore : real data store..
57063  });
57064  // set any options
57065  grid.render();
57066   
57067   * @constructor
57068  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57069  * The container MUST have some type of size defined for the grid to fill. The container will be
57070  * automatically set to position relative if it isn't already.
57071  * @param {Object} config A config object that sets properties on this grid.
57072  */
57073 Roo.grid.Calendar = function(container, config){
57074         // initialize the container
57075         this.container = Roo.get(container);
57076         this.container.update("");
57077         this.container.setStyle("overflow", "hidden");
57078     this.container.addClass('x-grid-container');
57079
57080     this.id = this.container.id;
57081
57082     Roo.apply(this, config);
57083     // check and correct shorthanded configs
57084     
57085     var rows = [];
57086     var d =1;
57087     for (var r = 0;r < 6;r++) {
57088         
57089         rows[r]=[];
57090         for (var c =0;c < 7;c++) {
57091             rows[r][c]= '';
57092         }
57093     }
57094     if (this.eventStore) {
57095         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57096         this.eventStore.on('load',this.onLoad, this);
57097         this.eventStore.on('beforeload',this.clearEvents, this);
57098          
57099     }
57100     
57101     this.dataSource = new Roo.data.Store({
57102             proxy: new Roo.data.MemoryProxy(rows),
57103             reader: new Roo.data.ArrayReader({}, [
57104                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57105     });
57106
57107     this.dataSource.load();
57108     this.ds = this.dataSource;
57109     this.ds.xmodule = this.xmodule || false;
57110     
57111     
57112     var cellRender = function(v,x,r)
57113     {
57114         return String.format(
57115             '<div class="fc-day  fc-widget-content"><div>' +
57116                 '<div class="fc-event-container"></div>' +
57117                 '<div class="fc-day-number">{0}</div>'+
57118                 
57119                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57120             '</div></div>', v);
57121     
57122     }
57123     
57124     
57125     this.colModel = new Roo.grid.ColumnModel( [
57126         {
57127             xtype: 'ColumnModel',
57128             xns: Roo.grid,
57129             dataIndex : 'weekday0',
57130             header : 'Sunday',
57131             renderer : cellRender
57132         },
57133         {
57134             xtype: 'ColumnModel',
57135             xns: Roo.grid,
57136             dataIndex : 'weekday1',
57137             header : 'Monday',
57138             renderer : cellRender
57139         },
57140         {
57141             xtype: 'ColumnModel',
57142             xns: Roo.grid,
57143             dataIndex : 'weekday2',
57144             header : 'Tuesday',
57145             renderer : cellRender
57146         },
57147         {
57148             xtype: 'ColumnModel',
57149             xns: Roo.grid,
57150             dataIndex : 'weekday3',
57151             header : 'Wednesday',
57152             renderer : cellRender
57153         },
57154         {
57155             xtype: 'ColumnModel',
57156             xns: Roo.grid,
57157             dataIndex : 'weekday4',
57158             header : 'Thursday',
57159             renderer : cellRender
57160         },
57161         {
57162             xtype: 'ColumnModel',
57163             xns: Roo.grid,
57164             dataIndex : 'weekday5',
57165             header : 'Friday',
57166             renderer : cellRender
57167         },
57168         {
57169             xtype: 'ColumnModel',
57170             xns: Roo.grid,
57171             dataIndex : 'weekday6',
57172             header : 'Saturday',
57173             renderer : cellRender
57174         }
57175     ]);
57176     this.cm = this.colModel;
57177     this.cm.xmodule = this.xmodule || false;
57178  
57179         
57180           
57181     //this.selModel = new Roo.grid.CellSelectionModel();
57182     //this.sm = this.selModel;
57183     //this.selModel.init(this);
57184     
57185     
57186     if(this.width){
57187         this.container.setWidth(this.width);
57188     }
57189
57190     if(this.height){
57191         this.container.setHeight(this.height);
57192     }
57193     /** @private */
57194         this.addEvents({
57195         // raw events
57196         /**
57197          * @event click
57198          * The raw click event for the entire grid.
57199          * @param {Roo.EventObject} e
57200          */
57201         "click" : true,
57202         /**
57203          * @event dblclick
57204          * The raw dblclick event for the entire grid.
57205          * @param {Roo.EventObject} e
57206          */
57207         "dblclick" : true,
57208         /**
57209          * @event contextmenu
57210          * The raw contextmenu event for the entire grid.
57211          * @param {Roo.EventObject} e
57212          */
57213         "contextmenu" : true,
57214         /**
57215          * @event mousedown
57216          * The raw mousedown event for the entire grid.
57217          * @param {Roo.EventObject} e
57218          */
57219         "mousedown" : true,
57220         /**
57221          * @event mouseup
57222          * The raw mouseup event for the entire grid.
57223          * @param {Roo.EventObject} e
57224          */
57225         "mouseup" : true,
57226         /**
57227          * @event mouseover
57228          * The raw mouseover event for the entire grid.
57229          * @param {Roo.EventObject} e
57230          */
57231         "mouseover" : true,
57232         /**
57233          * @event mouseout
57234          * The raw mouseout event for the entire grid.
57235          * @param {Roo.EventObject} e
57236          */
57237         "mouseout" : true,
57238         /**
57239          * @event keypress
57240          * The raw keypress event for the entire grid.
57241          * @param {Roo.EventObject} e
57242          */
57243         "keypress" : true,
57244         /**
57245          * @event keydown
57246          * The raw keydown event for the entire grid.
57247          * @param {Roo.EventObject} e
57248          */
57249         "keydown" : true,
57250
57251         // custom events
57252
57253         /**
57254          * @event cellclick
57255          * Fires when a cell is clicked
57256          * @param {Grid} this
57257          * @param {Number} rowIndex
57258          * @param {Number} columnIndex
57259          * @param {Roo.EventObject} e
57260          */
57261         "cellclick" : true,
57262         /**
57263          * @event celldblclick
57264          * Fires when a cell is double clicked
57265          * @param {Grid} this
57266          * @param {Number} rowIndex
57267          * @param {Number} columnIndex
57268          * @param {Roo.EventObject} e
57269          */
57270         "celldblclick" : true,
57271         /**
57272          * @event rowclick
57273          * Fires when a row is clicked
57274          * @param {Grid} this
57275          * @param {Number} rowIndex
57276          * @param {Roo.EventObject} e
57277          */
57278         "rowclick" : true,
57279         /**
57280          * @event rowdblclick
57281          * Fires when a row is double clicked
57282          * @param {Grid} this
57283          * @param {Number} rowIndex
57284          * @param {Roo.EventObject} e
57285          */
57286         "rowdblclick" : true,
57287         /**
57288          * @event headerclick
57289          * Fires when a header is clicked
57290          * @param {Grid} this
57291          * @param {Number} columnIndex
57292          * @param {Roo.EventObject} e
57293          */
57294         "headerclick" : true,
57295         /**
57296          * @event headerdblclick
57297          * Fires when a header cell is double clicked
57298          * @param {Grid} this
57299          * @param {Number} columnIndex
57300          * @param {Roo.EventObject} e
57301          */
57302         "headerdblclick" : true,
57303         /**
57304          * @event rowcontextmenu
57305          * Fires when a row is right clicked
57306          * @param {Grid} this
57307          * @param {Number} rowIndex
57308          * @param {Roo.EventObject} e
57309          */
57310         "rowcontextmenu" : true,
57311         /**
57312          * @event cellcontextmenu
57313          * Fires when a cell is right clicked
57314          * @param {Grid} this
57315          * @param {Number} rowIndex
57316          * @param {Number} cellIndex
57317          * @param {Roo.EventObject} e
57318          */
57319          "cellcontextmenu" : true,
57320         /**
57321          * @event headercontextmenu
57322          * Fires when a header is right clicked
57323          * @param {Grid} this
57324          * @param {Number} columnIndex
57325          * @param {Roo.EventObject} e
57326          */
57327         "headercontextmenu" : true,
57328         /**
57329          * @event bodyscroll
57330          * Fires when the body element is scrolled
57331          * @param {Number} scrollLeft
57332          * @param {Number} scrollTop
57333          */
57334         "bodyscroll" : true,
57335         /**
57336          * @event columnresize
57337          * Fires when the user resizes a column
57338          * @param {Number} columnIndex
57339          * @param {Number} newSize
57340          */
57341         "columnresize" : true,
57342         /**
57343          * @event columnmove
57344          * Fires when the user moves a column
57345          * @param {Number} oldIndex
57346          * @param {Number} newIndex
57347          */
57348         "columnmove" : true,
57349         /**
57350          * @event startdrag
57351          * Fires when row(s) start being dragged
57352          * @param {Grid} this
57353          * @param {Roo.GridDD} dd The drag drop object
57354          * @param {event} e The raw browser event
57355          */
57356         "startdrag" : true,
57357         /**
57358          * @event enddrag
57359          * Fires when a drag operation is complete
57360          * @param {Grid} this
57361          * @param {Roo.GridDD} dd The drag drop object
57362          * @param {event} e The raw browser event
57363          */
57364         "enddrag" : true,
57365         /**
57366          * @event dragdrop
57367          * Fires when dragged row(s) are dropped on a valid DD target
57368          * @param {Grid} this
57369          * @param {Roo.GridDD} dd The drag drop object
57370          * @param {String} targetId The target drag drop object
57371          * @param {event} e The raw browser event
57372          */
57373         "dragdrop" : true,
57374         /**
57375          * @event dragover
57376          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57377          * @param {Grid} this
57378          * @param {Roo.GridDD} dd The drag drop object
57379          * @param {String} targetId The target drag drop object
57380          * @param {event} e The raw browser event
57381          */
57382         "dragover" : true,
57383         /**
57384          * @event dragenter
57385          *  Fires when the dragged row(s) first cross another DD target while being dragged
57386          * @param {Grid} this
57387          * @param {Roo.GridDD} dd The drag drop object
57388          * @param {String} targetId The target drag drop object
57389          * @param {event} e The raw browser event
57390          */
57391         "dragenter" : true,
57392         /**
57393          * @event dragout
57394          * Fires when the dragged row(s) leave another DD target while being dragged
57395          * @param {Grid} this
57396          * @param {Roo.GridDD} dd The drag drop object
57397          * @param {String} targetId The target drag drop object
57398          * @param {event} e The raw browser event
57399          */
57400         "dragout" : true,
57401         /**
57402          * @event rowclass
57403          * Fires when a row is rendered, so you can change add a style to it.
57404          * @param {GridView} gridview   The grid view
57405          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57406          */
57407         'rowclass' : true,
57408
57409         /**
57410          * @event render
57411          * Fires when the grid is rendered
57412          * @param {Grid} grid
57413          */
57414         'render' : true,
57415             /**
57416              * @event select
57417              * Fires when a date is selected
57418              * @param {DatePicker} this
57419              * @param {Date} date The selected date
57420              */
57421         'select': true,
57422         /**
57423              * @event monthchange
57424              * Fires when the displayed month changes 
57425              * @param {DatePicker} this
57426              * @param {Date} date The selected month
57427              */
57428         'monthchange': true,
57429         /**
57430              * @event evententer
57431              * Fires when mouse over an event
57432              * @param {Calendar} this
57433              * @param {event} Event
57434              */
57435         'evententer': true,
57436         /**
57437              * @event eventleave
57438              * Fires when the mouse leaves an
57439              * @param {Calendar} this
57440              * @param {event}
57441              */
57442         'eventleave': true,
57443         /**
57444              * @event eventclick
57445              * Fires when the mouse click an
57446              * @param {Calendar} this
57447              * @param {event}
57448              */
57449         'eventclick': true,
57450         /**
57451              * @event eventrender
57452              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57453              * @param {Calendar} this
57454              * @param {data} data to be modified
57455              */
57456         'eventrender': true
57457         
57458     });
57459
57460     Roo.grid.Grid.superclass.constructor.call(this);
57461     this.on('render', function() {
57462         this.view.el.addClass('x-grid-cal'); 
57463         
57464         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57465
57466     },this);
57467     
57468     if (!Roo.grid.Calendar.style) {
57469         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57470             
57471             
57472             '.x-grid-cal .x-grid-col' :  {
57473                 height: 'auto !important',
57474                 'vertical-align': 'top'
57475             },
57476             '.x-grid-cal  .fc-event-hori' : {
57477                 height: '14px'
57478             }
57479              
57480             
57481         }, Roo.id());
57482     }
57483
57484     
57485     
57486 };
57487 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57488     /**
57489      * @cfg {Store} eventStore The store that loads events.
57490      */
57491     eventStore : 25,
57492
57493      
57494     activeDate : false,
57495     startDay : 0,
57496     autoWidth : true,
57497     monitorWindowResize : false,
57498
57499     
57500     resizeColumns : function() {
57501         var col = (this.view.el.getWidth() / 7) - 3;
57502         // loop through cols, and setWidth
57503         for(var i =0 ; i < 7 ; i++){
57504             this.cm.setColumnWidth(i, col);
57505         }
57506     },
57507      setDate :function(date) {
57508         
57509         Roo.log('setDate?');
57510         
57511         this.resizeColumns();
57512         var vd = this.activeDate;
57513         this.activeDate = date;
57514 //        if(vd && this.el){
57515 //            var t = date.getTime();
57516 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57517 //                Roo.log('using add remove');
57518 //                
57519 //                this.fireEvent('monthchange', this, date);
57520 //                
57521 //                this.cells.removeClass("fc-state-highlight");
57522 //                this.cells.each(function(c){
57523 //                   if(c.dateValue == t){
57524 //                       c.addClass("fc-state-highlight");
57525 //                       setTimeout(function(){
57526 //                            try{c.dom.firstChild.focus();}catch(e){}
57527 //                       }, 50);
57528 //                       return false;
57529 //                   }
57530 //                   return true;
57531 //                });
57532 //                return;
57533 //            }
57534 //        }
57535         
57536         var days = date.getDaysInMonth();
57537         
57538         var firstOfMonth = date.getFirstDateOfMonth();
57539         var startingPos = firstOfMonth.getDay()-this.startDay;
57540         
57541         if(startingPos < this.startDay){
57542             startingPos += 7;
57543         }
57544         
57545         var pm = date.add(Date.MONTH, -1);
57546         var prevStart = pm.getDaysInMonth()-startingPos;
57547 //        
57548         
57549         
57550         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57551         
57552         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57553         //this.cells.addClassOnOver('fc-state-hover');
57554         
57555         var cells = this.cells.elements;
57556         var textEls = this.textNodes;
57557         
57558         //Roo.each(cells, function(cell){
57559         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57560         //});
57561         
57562         days += startingPos;
57563
57564         // convert everything to numbers so it's fast
57565         var day = 86400000;
57566         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57567         //Roo.log(d);
57568         //Roo.log(pm);
57569         //Roo.log(prevStart);
57570         
57571         var today = new Date().clearTime().getTime();
57572         var sel = date.clearTime().getTime();
57573         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57574         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57575         var ddMatch = this.disabledDatesRE;
57576         var ddText = this.disabledDatesText;
57577         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57578         var ddaysText = this.disabledDaysText;
57579         var format = this.format;
57580         
57581         var setCellClass = function(cal, cell){
57582             
57583             //Roo.log('set Cell Class');
57584             cell.title = "";
57585             var t = d.getTime();
57586             
57587             //Roo.log(d);
57588             
57589             
57590             cell.dateValue = t;
57591             if(t == today){
57592                 cell.className += " fc-today";
57593                 cell.className += " fc-state-highlight";
57594                 cell.title = cal.todayText;
57595             }
57596             if(t == sel){
57597                 // disable highlight in other month..
57598                 cell.className += " fc-state-highlight";
57599                 
57600             }
57601             // disabling
57602             if(t < min) {
57603                 //cell.className = " fc-state-disabled";
57604                 cell.title = cal.minText;
57605                 return;
57606             }
57607             if(t > max) {
57608                 //cell.className = " fc-state-disabled";
57609                 cell.title = cal.maxText;
57610                 return;
57611             }
57612             if(ddays){
57613                 if(ddays.indexOf(d.getDay()) != -1){
57614                     // cell.title = ddaysText;
57615                    // cell.className = " fc-state-disabled";
57616                 }
57617             }
57618             if(ddMatch && format){
57619                 var fvalue = d.dateFormat(format);
57620                 if(ddMatch.test(fvalue)){
57621                     cell.title = ddText.replace("%0", fvalue);
57622                    cell.className = " fc-state-disabled";
57623                 }
57624             }
57625             
57626             if (!cell.initialClassName) {
57627                 cell.initialClassName = cell.dom.className;
57628             }
57629             
57630             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57631         };
57632
57633         var i = 0;
57634         
57635         for(; i < startingPos; i++) {
57636             cells[i].dayName =  (++prevStart);
57637             Roo.log(textEls[i]);
57638             d.setDate(d.getDate()+1);
57639             
57640             //cells[i].className = "fc-past fc-other-month";
57641             setCellClass(this, cells[i]);
57642         }
57643         
57644         var intDay = 0;
57645         
57646         for(; i < days; i++){
57647             intDay = i - startingPos + 1;
57648             cells[i].dayName =  (intDay);
57649             d.setDate(d.getDate()+1);
57650             
57651             cells[i].className = ''; // "x-date-active";
57652             setCellClass(this, cells[i]);
57653         }
57654         var extraDays = 0;
57655         
57656         for(; i < 42; i++) {
57657             //textEls[i].innerHTML = (++extraDays);
57658             
57659             d.setDate(d.getDate()+1);
57660             cells[i].dayName = (++extraDays);
57661             cells[i].className = "fc-future fc-other-month";
57662             setCellClass(this, cells[i]);
57663         }
57664         
57665         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57666         
57667         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57668         
57669         // this will cause all the cells to mis
57670         var rows= [];
57671         var i =0;
57672         for (var r = 0;r < 6;r++) {
57673             for (var c =0;c < 7;c++) {
57674                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57675             }    
57676         }
57677         
57678         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57679         for(i=0;i<cells.length;i++) {
57680             
57681             this.cells.elements[i].dayName = cells[i].dayName ;
57682             this.cells.elements[i].className = cells[i].className;
57683             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57684             this.cells.elements[i].title = cells[i].title ;
57685             this.cells.elements[i].dateValue = cells[i].dateValue ;
57686         }
57687         
57688         
57689         
57690         
57691         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57692         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57693         
57694         ////if(totalRows != 6){
57695             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57696            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57697        // }
57698         
57699         this.fireEvent('monthchange', this, date);
57700         
57701         
57702     },
57703  /**
57704      * Returns the grid's SelectionModel.
57705      * @return {SelectionModel}
57706      */
57707     getSelectionModel : function(){
57708         if(!this.selModel){
57709             this.selModel = new Roo.grid.CellSelectionModel();
57710         }
57711         return this.selModel;
57712     },
57713
57714     load: function() {
57715         this.eventStore.load()
57716         
57717         
57718         
57719     },
57720     
57721     findCell : function(dt) {
57722         dt = dt.clearTime().getTime();
57723         var ret = false;
57724         this.cells.each(function(c){
57725             //Roo.log("check " +c.dateValue + '?=' + dt);
57726             if(c.dateValue == dt){
57727                 ret = c;
57728                 return false;
57729             }
57730             return true;
57731         });
57732         
57733         return ret;
57734     },
57735     
57736     findCells : function(rec) {
57737         var s = rec.data.start_dt.clone().clearTime().getTime();
57738        // Roo.log(s);
57739         var e= rec.data.end_dt.clone().clearTime().getTime();
57740        // Roo.log(e);
57741         var ret = [];
57742         this.cells.each(function(c){
57743              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57744             
57745             if(c.dateValue > e){
57746                 return ;
57747             }
57748             if(c.dateValue < s){
57749                 return ;
57750             }
57751             ret.push(c);
57752         });
57753         
57754         return ret;    
57755     },
57756     
57757     findBestRow: function(cells)
57758     {
57759         var ret = 0;
57760         
57761         for (var i =0 ; i < cells.length;i++) {
57762             ret  = Math.max(cells[i].rows || 0,ret);
57763         }
57764         return ret;
57765         
57766     },
57767     
57768     
57769     addItem : function(rec)
57770     {
57771         // look for vertical location slot in
57772         var cells = this.findCells(rec);
57773         
57774         rec.row = this.findBestRow(cells);
57775         
57776         // work out the location.
57777         
57778         var crow = false;
57779         var rows = [];
57780         for(var i =0; i < cells.length; i++) {
57781             if (!crow) {
57782                 crow = {
57783                     start : cells[i],
57784                     end :  cells[i]
57785                 };
57786                 continue;
57787             }
57788             if (crow.start.getY() == cells[i].getY()) {
57789                 // on same row.
57790                 crow.end = cells[i];
57791                 continue;
57792             }
57793             // different row.
57794             rows.push(crow);
57795             crow = {
57796                 start: cells[i],
57797                 end : cells[i]
57798             };
57799             
57800         }
57801         
57802         rows.push(crow);
57803         rec.els = [];
57804         rec.rows = rows;
57805         rec.cells = cells;
57806         for (var i = 0; i < cells.length;i++) {
57807             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57808             
57809         }
57810         
57811         
57812     },
57813     
57814     clearEvents: function() {
57815         
57816         if (!this.eventStore.getCount()) {
57817             return;
57818         }
57819         // reset number of rows in cells.
57820         Roo.each(this.cells.elements, function(c){
57821             c.rows = 0;
57822         });
57823         
57824         this.eventStore.each(function(e) {
57825             this.clearEvent(e);
57826         },this);
57827         
57828     },
57829     
57830     clearEvent : function(ev)
57831     {
57832         if (ev.els) {
57833             Roo.each(ev.els, function(el) {
57834                 el.un('mouseenter' ,this.onEventEnter, this);
57835                 el.un('mouseleave' ,this.onEventLeave, this);
57836                 el.remove();
57837             },this);
57838             ev.els = [];
57839         }
57840     },
57841     
57842     
57843     renderEvent : function(ev,ctr) {
57844         if (!ctr) {
57845              ctr = this.view.el.select('.fc-event-container',true).first();
57846         }
57847         
57848          
57849         this.clearEvent(ev);
57850             //code
57851        
57852         
57853         
57854         ev.els = [];
57855         var cells = ev.cells;
57856         var rows = ev.rows;
57857         this.fireEvent('eventrender', this, ev);
57858         
57859         for(var i =0; i < rows.length; i++) {
57860             
57861             cls = '';
57862             if (i == 0) {
57863                 cls += ' fc-event-start';
57864             }
57865             if ((i+1) == rows.length) {
57866                 cls += ' fc-event-end';
57867             }
57868             
57869             //Roo.log(ev.data);
57870             // how many rows should it span..
57871             var cg = this.eventTmpl.append(ctr,Roo.apply({
57872                 fccls : cls
57873                 
57874             }, ev.data) , true);
57875             
57876             
57877             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57878             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57879             cg.on('click', this.onEventClick, this, ev);
57880             
57881             ev.els.push(cg);
57882             
57883             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57884             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57885             //Roo.log(cg);
57886              
57887             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57888             cg.setWidth(ebox.right - sbox.x -2);
57889         }
57890     },
57891     
57892     renderEvents: function()
57893     {   
57894         // first make sure there is enough space..
57895         
57896         if (!this.eventTmpl) {
57897             this.eventTmpl = new Roo.Template(
57898                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57899                     '<div class="fc-event-inner">' +
57900                         '<span class="fc-event-time">{time}</span>' +
57901                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57902                     '</div>' +
57903                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57904                 '</div>'
57905             );
57906                 
57907         }
57908                
57909         
57910         
57911         this.cells.each(function(c) {
57912             //Roo.log(c.select('.fc-day-content div',true).first());
57913             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57914         });
57915         
57916         var ctr = this.view.el.select('.fc-event-container',true).first();
57917         
57918         var cls;
57919         this.eventStore.each(function(ev){
57920             
57921             this.renderEvent(ev);
57922              
57923              
57924         }, this);
57925         this.view.layout();
57926         
57927     },
57928     
57929     onEventEnter: function (e, el,event,d) {
57930         this.fireEvent('evententer', this, el, event);
57931     },
57932     
57933     onEventLeave: function (e, el,event,d) {
57934         this.fireEvent('eventleave', this, el, event);
57935     },
57936     
57937     onEventClick: function (e, el,event,d) {
57938         this.fireEvent('eventclick', this, el, event);
57939     },
57940     
57941     onMonthChange: function () {
57942         this.store.load();
57943     },
57944     
57945     onLoad: function () {
57946         
57947         //Roo.log('calendar onload');
57948 //         
57949         if(this.eventStore.getCount() > 0){
57950             
57951            
57952             
57953             this.eventStore.each(function(d){
57954                 
57955                 
57956                 // FIXME..
57957                 var add =   d.data;
57958                 if (typeof(add.end_dt) == 'undefined')  {
57959                     Roo.log("Missing End time in calendar data: ");
57960                     Roo.log(d);
57961                     return;
57962                 }
57963                 if (typeof(add.start_dt) == 'undefined')  {
57964                     Roo.log("Missing Start time in calendar data: ");
57965                     Roo.log(d);
57966                     return;
57967                 }
57968                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57969                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57970                 add.id = add.id || d.id;
57971                 add.title = add.title || '??';
57972                 
57973                 this.addItem(d);
57974                 
57975              
57976             },this);
57977         }
57978         
57979         this.renderEvents();
57980     }
57981     
57982
57983 });
57984 /*
57985  grid : {
57986                 xtype: 'Grid',
57987                 xns: Roo.grid,
57988                 listeners : {
57989                     render : function ()
57990                     {
57991                         _this.grid = this;
57992                         
57993                         if (!this.view.el.hasClass('course-timesheet')) {
57994                             this.view.el.addClass('course-timesheet');
57995                         }
57996                         if (this.tsStyle) {
57997                             this.ds.load({});
57998                             return; 
57999                         }
58000                         Roo.log('width');
58001                         Roo.log(_this.grid.view.el.getWidth());
58002                         
58003                         
58004                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58005                             '.course-timesheet .x-grid-row' : {
58006                                 height: '80px'
58007                             },
58008                             '.x-grid-row td' : {
58009                                 'vertical-align' : 0
58010                             },
58011                             '.course-edit-link' : {
58012                                 'color' : 'blue',
58013                                 'text-overflow' : 'ellipsis',
58014                                 'overflow' : 'hidden',
58015                                 'white-space' : 'nowrap',
58016                                 'cursor' : 'pointer'
58017                             },
58018                             '.sub-link' : {
58019                                 'color' : 'green'
58020                             },
58021                             '.de-act-sup-link' : {
58022                                 'color' : 'purple',
58023                                 'text-decoration' : 'line-through'
58024                             },
58025                             '.de-act-link' : {
58026                                 'color' : 'red',
58027                                 'text-decoration' : 'line-through'
58028                             },
58029                             '.course-timesheet .course-highlight' : {
58030                                 'border-top-style': 'dashed !important',
58031                                 'border-bottom-bottom': 'dashed !important'
58032                             },
58033                             '.course-timesheet .course-item' : {
58034                                 'font-family'   : 'tahoma, arial, helvetica',
58035                                 'font-size'     : '11px',
58036                                 'overflow'      : 'hidden',
58037                                 'padding-left'  : '10px',
58038                                 'padding-right' : '10px',
58039                                 'padding-top' : '10px' 
58040                             }
58041                             
58042                         }, Roo.id());
58043                                 this.ds.load({});
58044                     }
58045                 },
58046                 autoWidth : true,
58047                 monitorWindowResize : false,
58048                 cellrenderer : function(v,x,r)
58049                 {
58050                     return v;
58051                 },
58052                 sm : {
58053                     xtype: 'CellSelectionModel',
58054                     xns: Roo.grid
58055                 },
58056                 dataSource : {
58057                     xtype: 'Store',
58058                     xns: Roo.data,
58059                     listeners : {
58060                         beforeload : function (_self, options)
58061                         {
58062                             options.params = options.params || {};
58063                             options.params._month = _this.monthField.getValue();
58064                             options.params.limit = 9999;
58065                             options.params['sort'] = 'when_dt';    
58066                             options.params['dir'] = 'ASC';    
58067                             this.proxy.loadResponse = this.loadResponse;
58068                             Roo.log("load?");
58069                             //this.addColumns();
58070                         },
58071                         load : function (_self, records, options)
58072                         {
58073                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58074                                 // if you click on the translation.. you can edit it...
58075                                 var el = Roo.get(this);
58076                                 var id = el.dom.getAttribute('data-id');
58077                                 var d = el.dom.getAttribute('data-date');
58078                                 var t = el.dom.getAttribute('data-time');
58079                                 //var id = this.child('span').dom.textContent;
58080                                 
58081                                 //Roo.log(this);
58082                                 Pman.Dialog.CourseCalendar.show({
58083                                     id : id,
58084                                     when_d : d,
58085                                     when_t : t,
58086                                     productitem_active : id ? 1 : 0
58087                                 }, function() {
58088                                     _this.grid.ds.load({});
58089                                 });
58090                            
58091                            });
58092                            
58093                            _this.panel.fireEvent('resize', [ '', '' ]);
58094                         }
58095                     },
58096                     loadResponse : function(o, success, response){
58097                             // this is overridden on before load..
58098                             
58099                             Roo.log("our code?");       
58100                             //Roo.log(success);
58101                             //Roo.log(response)
58102                             delete this.activeRequest;
58103                             if(!success){
58104                                 this.fireEvent("loadexception", this, o, response);
58105                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58106                                 return;
58107                             }
58108                             var result;
58109                             try {
58110                                 result = o.reader.read(response);
58111                             }catch(e){
58112                                 Roo.log("load exception?");
58113                                 this.fireEvent("loadexception", this, o, response, e);
58114                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58115                                 return;
58116                             }
58117                             Roo.log("ready...");        
58118                             // loop through result.records;
58119                             // and set this.tdate[date] = [] << array of records..
58120                             _this.tdata  = {};
58121                             Roo.each(result.records, function(r){
58122                                 //Roo.log(r.data);
58123                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58124                                     _this.tdata[r.data.when_dt.format('j')] = [];
58125                                 }
58126                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58127                             });
58128                             
58129                             //Roo.log(_this.tdata);
58130                             
58131                             result.records = [];
58132                             result.totalRecords = 6;
58133                     
58134                             // let's generate some duumy records for the rows.
58135                             //var st = _this.dateField.getValue();
58136                             
58137                             // work out monday..
58138                             //st = st.add(Date.DAY, -1 * st.format('w'));
58139                             
58140                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58141                             
58142                             var firstOfMonth = date.getFirstDayOfMonth();
58143                             var days = date.getDaysInMonth();
58144                             var d = 1;
58145                             var firstAdded = false;
58146                             for (var i = 0; i < result.totalRecords ; i++) {
58147                                 //var d= st.add(Date.DAY, i);
58148                                 var row = {};
58149                                 var added = 0;
58150                                 for(var w = 0 ; w < 7 ; w++){
58151                                     if(!firstAdded && firstOfMonth != w){
58152                                         continue;
58153                                     }
58154                                     if(d > days){
58155                                         continue;
58156                                     }
58157                                     firstAdded = true;
58158                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58159                                     row['weekday'+w] = String.format(
58160                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58161                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58162                                                     d,
58163                                                     date.format('Y-m-')+dd
58164                                                 );
58165                                     added++;
58166                                     if(typeof(_this.tdata[d]) != 'undefined'){
58167                                         Roo.each(_this.tdata[d], function(r){
58168                                             var is_sub = '';
58169                                             var deactive = '';
58170                                             var id = r.id;
58171                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58172                                             if(r.parent_id*1>0){
58173                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58174                                                 id = r.parent_id;
58175                                             }
58176                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58177                                                 deactive = 'de-act-link';
58178                                             }
58179                                             
58180                                             row['weekday'+w] += String.format(
58181                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58182                                                     id, //0
58183                                                     r.product_id_name, //1
58184                                                     r.when_dt.format('h:ia'), //2
58185                                                     is_sub, //3
58186                                                     deactive, //4
58187                                                     desc // 5
58188                                             );
58189                                         });
58190                                     }
58191                                     d++;
58192                                 }
58193                                 
58194                                 // only do this if something added..
58195                                 if(added > 0){ 
58196                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58197                                 }
58198                                 
58199                                 
58200                                 // push it twice. (second one with an hour..
58201                                 
58202                             }
58203                             //Roo.log(result);
58204                             this.fireEvent("load", this, o, o.request.arg);
58205                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58206                         },
58207                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58208                     proxy : {
58209                         xtype: 'HttpProxy',
58210                         xns: Roo.data,
58211                         method : 'GET',
58212                         url : baseURL + '/Roo/Shop_course.php'
58213                     },
58214                     reader : {
58215                         xtype: 'JsonReader',
58216                         xns: Roo.data,
58217                         id : 'id',
58218                         fields : [
58219                             {
58220                                 'name': 'id',
58221                                 'type': 'int'
58222                             },
58223                             {
58224                                 'name': 'when_dt',
58225                                 'type': 'string'
58226                             },
58227                             {
58228                                 'name': 'end_dt',
58229                                 'type': 'string'
58230                             },
58231                             {
58232                                 'name': 'parent_id',
58233                                 'type': 'int'
58234                             },
58235                             {
58236                                 'name': 'product_id',
58237                                 'type': 'int'
58238                             },
58239                             {
58240                                 'name': 'productitem_id',
58241                                 'type': 'int'
58242                             },
58243                             {
58244                                 'name': 'guid',
58245                                 'type': 'int'
58246                             }
58247                         ]
58248                     }
58249                 },
58250                 toolbar : {
58251                     xtype: 'Toolbar',
58252                     xns: Roo,
58253                     items : [
58254                         {
58255                             xtype: 'Button',
58256                             xns: Roo.Toolbar,
58257                             listeners : {
58258                                 click : function (_self, e)
58259                                 {
58260                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58261                                     sd.setMonth(sd.getMonth()-1);
58262                                     _this.monthField.setValue(sd.format('Y-m-d'));
58263                                     _this.grid.ds.load({});
58264                                 }
58265                             },
58266                             text : "Back"
58267                         },
58268                         {
58269                             xtype: 'Separator',
58270                             xns: Roo.Toolbar
58271                         },
58272                         {
58273                             xtype: 'MonthField',
58274                             xns: Roo.form,
58275                             listeners : {
58276                                 render : function (_self)
58277                                 {
58278                                     _this.monthField = _self;
58279                                    // _this.monthField.set  today
58280                                 },
58281                                 select : function (combo, date)
58282                                 {
58283                                     _this.grid.ds.load({});
58284                                 }
58285                             },
58286                             value : (function() { return new Date(); })()
58287                         },
58288                         {
58289                             xtype: 'Separator',
58290                             xns: Roo.Toolbar
58291                         },
58292                         {
58293                             xtype: 'TextItem',
58294                             xns: Roo.Toolbar,
58295                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58296                         },
58297                         {
58298                             xtype: 'Fill',
58299                             xns: Roo.Toolbar
58300                         },
58301                         {
58302                             xtype: 'Button',
58303                             xns: Roo.Toolbar,
58304                             listeners : {
58305                                 click : function (_self, e)
58306                                 {
58307                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58308                                     sd.setMonth(sd.getMonth()+1);
58309                                     _this.monthField.setValue(sd.format('Y-m-d'));
58310                                     _this.grid.ds.load({});
58311                                 }
58312                             },
58313                             text : "Next"
58314                         }
58315                     ]
58316                 },
58317                  
58318             }
58319         };
58320         
58321         *//*
58322  * Based on:
58323  * Ext JS Library 1.1.1
58324  * Copyright(c) 2006-2007, Ext JS, LLC.
58325  *
58326  * Originally Released Under LGPL - original licence link has changed is not relivant.
58327  *
58328  * Fork - LGPL
58329  * <script type="text/javascript">
58330  */
58331  
58332 /**
58333  * @class Roo.LoadMask
58334  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58335  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58336  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58337  * element's UpdateManager load indicator and will be destroyed after the initial load.
58338  * @constructor
58339  * Create a new LoadMask
58340  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58341  * @param {Object} config The config object
58342  */
58343 Roo.LoadMask = function(el, config){
58344     this.el = Roo.get(el);
58345     Roo.apply(this, config);
58346     if(this.store){
58347         this.store.on('beforeload', this.onBeforeLoad, this);
58348         this.store.on('load', this.onLoad, this);
58349         this.store.on('loadexception', this.onLoadException, this);
58350         this.removeMask = false;
58351     }else{
58352         var um = this.el.getUpdateManager();
58353         um.showLoadIndicator = false; // disable the default indicator
58354         um.on('beforeupdate', this.onBeforeLoad, this);
58355         um.on('update', this.onLoad, this);
58356         um.on('failure', this.onLoad, this);
58357         this.removeMask = true;
58358     }
58359 };
58360
58361 Roo.LoadMask.prototype = {
58362     /**
58363      * @cfg {Boolean} removeMask
58364      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58365      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58366      */
58367     /**
58368      * @cfg {String} msg
58369      * The text to display in a centered loading message box (defaults to 'Loading...')
58370      */
58371     msg : 'Loading...',
58372     /**
58373      * @cfg {String} msgCls
58374      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58375      */
58376     msgCls : 'x-mask-loading',
58377
58378     /**
58379      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58380      * @type Boolean
58381      */
58382     disabled: false,
58383
58384     /**
58385      * Disables the mask to prevent it from being displayed
58386      */
58387     disable : function(){
58388        this.disabled = true;
58389     },
58390
58391     /**
58392      * Enables the mask so that it can be displayed
58393      */
58394     enable : function(){
58395         this.disabled = false;
58396     },
58397     
58398     onLoadException : function()
58399     {
58400         Roo.log(arguments);
58401         
58402         if (typeof(arguments[3]) != 'undefined') {
58403             Roo.MessageBox.alert("Error loading",arguments[3]);
58404         } 
58405         /*
58406         try {
58407             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58408                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58409             }   
58410         } catch(e) {
58411             
58412         }
58413         */
58414     
58415         
58416         
58417         this.el.unmask(this.removeMask);
58418     },
58419     // private
58420     onLoad : function()
58421     {
58422         this.el.unmask(this.removeMask);
58423     },
58424
58425     // private
58426     onBeforeLoad : function(){
58427         if(!this.disabled){
58428             this.el.mask(this.msg, this.msgCls);
58429         }
58430     },
58431
58432     // private
58433     destroy : function(){
58434         if(this.store){
58435             this.store.un('beforeload', this.onBeforeLoad, this);
58436             this.store.un('load', this.onLoad, this);
58437             this.store.un('loadexception', this.onLoadException, this);
58438         }else{
58439             var um = this.el.getUpdateManager();
58440             um.un('beforeupdate', this.onBeforeLoad, this);
58441             um.un('update', this.onLoad, this);
58442             um.un('failure', this.onLoad, this);
58443         }
58444     }
58445 };/*
58446  * Based on:
58447  * Ext JS Library 1.1.1
58448  * Copyright(c) 2006-2007, Ext JS, LLC.
58449  *
58450  * Originally Released Under LGPL - original licence link has changed is not relivant.
58451  *
58452  * Fork - LGPL
58453  * <script type="text/javascript">
58454  */
58455
58456
58457 /**
58458  * @class Roo.XTemplate
58459  * @extends Roo.Template
58460  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58461 <pre><code>
58462 var t = new Roo.XTemplate(
58463         '&lt;select name="{name}"&gt;',
58464                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58465         '&lt;/select&gt;'
58466 );
58467  
58468 // then append, applying the master template values
58469  </code></pre>
58470  *
58471  * Supported features:
58472  *
58473  *  Tags:
58474
58475 <pre><code>
58476       {a_variable} - output encoded.
58477       {a_variable.format:("Y-m-d")} - call a method on the variable
58478       {a_variable:raw} - unencoded output
58479       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58480       {a_variable:this.method_on_template(...)} - call a method on the template object.
58481  
58482 </code></pre>
58483  *  The tpl tag:
58484 <pre><code>
58485         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58486         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58487         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58488         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58489   
58490         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58491         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58492 </code></pre>
58493  *      
58494  */
58495 Roo.XTemplate = function()
58496 {
58497     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58498     if (this.html) {
58499         this.compile();
58500     }
58501 };
58502
58503
58504 Roo.extend(Roo.XTemplate, Roo.Template, {
58505
58506     /**
58507      * The various sub templates
58508      */
58509     tpls : false,
58510     /**
58511      *
58512      * basic tag replacing syntax
58513      * WORD:WORD()
58514      *
58515      * // you can fake an object call by doing this
58516      *  x.t:(test,tesT) 
58517      * 
58518      */
58519     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58520
58521     /**
58522      * compile the template
58523      *
58524      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58525      *
58526      */
58527     compile: function()
58528     {
58529         var s = this.html;
58530      
58531         s = ['<tpl>', s, '</tpl>'].join('');
58532     
58533         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58534             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58535             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58536             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58537             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58538             m,
58539             id     = 0,
58540             tpls   = [];
58541     
58542         while(true == !!(m = s.match(re))){
58543             var forMatch   = m[0].match(nameRe),
58544                 ifMatch   = m[0].match(ifRe),
58545                 execMatch   = m[0].match(execRe),
58546                 namedMatch   = m[0].match(namedRe),
58547                 
58548                 exp  = null, 
58549                 fn   = null,
58550                 exec = null,
58551                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58552                 
58553             if (ifMatch) {
58554                 // if - puts fn into test..
58555                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58556                 if(exp){
58557                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58558                 }
58559             }
58560             
58561             if (execMatch) {
58562                 // exec - calls a function... returns empty if true is  returned.
58563                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58564                 if(exp){
58565                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58566                 }
58567             }
58568             
58569             
58570             if (name) {
58571                 // for = 
58572                 switch(name){
58573                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58574                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58575                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58576                 }
58577             }
58578             var uid = namedMatch ? namedMatch[1] : id;
58579             
58580             
58581             tpls.push({
58582                 id:     namedMatch ? namedMatch[1] : id,
58583                 target: name,
58584                 exec:   exec,
58585                 test:   fn,
58586                 body:   m[1] || ''
58587             });
58588             if (namedMatch) {
58589                 s = s.replace(m[0], '');
58590             } else { 
58591                 s = s.replace(m[0], '{xtpl'+ id + '}');
58592             }
58593             ++id;
58594         }
58595         this.tpls = [];
58596         for(var i = tpls.length-1; i >= 0; --i){
58597             this.compileTpl(tpls[i]);
58598             this.tpls[tpls[i].id] = tpls[i];
58599         }
58600         this.master = tpls[tpls.length-1];
58601         return this;
58602     },
58603     /**
58604      * same as applyTemplate, except it's done to one of the subTemplates
58605      * when using named templates, you can do:
58606      *
58607      * var str = pl.applySubTemplate('your-name', values);
58608      *
58609      * 
58610      * @param {Number} id of the template
58611      * @param {Object} values to apply to template
58612      * @param {Object} parent (normaly the instance of this object)
58613      */
58614     applySubTemplate : function(id, values, parent)
58615     {
58616         
58617         
58618         var t = this.tpls[id];
58619         
58620         
58621         try { 
58622             if(t.test && !t.test.call(this, values, parent)){
58623                 return '';
58624             }
58625         } catch(e) {
58626             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58627             Roo.log(e.toString());
58628             Roo.log(t.test);
58629             return ''
58630         }
58631         try { 
58632             
58633             if(t.exec && t.exec.call(this, values, parent)){
58634                 return '';
58635             }
58636         } catch(e) {
58637             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58638             Roo.log(e.toString());
58639             Roo.log(t.exec);
58640             return ''
58641         }
58642         try {
58643             var vs = t.target ? t.target.call(this, values, parent) : values;
58644             parent = t.target ? values : parent;
58645             if(t.target && vs instanceof Array){
58646                 var buf = [];
58647                 for(var i = 0, len = vs.length; i < len; i++){
58648                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58649                 }
58650                 return buf.join('');
58651             }
58652             return t.compiled.call(this, vs, parent);
58653         } catch (e) {
58654             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58655             Roo.log(e.toString());
58656             Roo.log(t.compiled);
58657             return '';
58658         }
58659     },
58660
58661     compileTpl : function(tpl)
58662     {
58663         var fm = Roo.util.Format;
58664         var useF = this.disableFormats !== true;
58665         var sep = Roo.isGecko ? "+" : ",";
58666         var undef = function(str) {
58667             Roo.log("Property not found :"  + str);
58668             return '';
58669         };
58670         
58671         var fn = function(m, name, format, args)
58672         {
58673             //Roo.log(arguments);
58674             args = args ? args.replace(/\\'/g,"'") : args;
58675             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58676             if (typeof(format) == 'undefined') {
58677                 format= 'htmlEncode';
58678             }
58679             if (format == 'raw' ) {
58680                 format = false;
58681             }
58682             
58683             if(name.substr(0, 4) == 'xtpl'){
58684                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58685             }
58686             
58687             // build an array of options to determine if value is undefined..
58688             
58689             // basically get 'xxxx.yyyy' then do
58690             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58691             //    (function () { Roo.log("Property not found"); return ''; })() :
58692             //    ......
58693             
58694             var udef_ar = [];
58695             var lookfor = '';
58696             Roo.each(name.split('.'), function(st) {
58697                 lookfor += (lookfor.length ? '.': '') + st;
58698                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58699             });
58700             
58701             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58702             
58703             
58704             if(format && useF){
58705                 
58706                 args = args ? ',' + args : "";
58707                  
58708                 if(format.substr(0, 5) != "this."){
58709                     format = "fm." + format + '(';
58710                 }else{
58711                     format = 'this.call("'+ format.substr(5) + '", ';
58712                     args = ", values";
58713                 }
58714                 
58715                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58716             }
58717              
58718             if (args.length) {
58719                 // called with xxyx.yuu:(test,test)
58720                 // change to ()
58721                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58722             }
58723             // raw.. - :raw modifier..
58724             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58725             
58726         };
58727         var body;
58728         // branched to use + in gecko and [].join() in others
58729         if(Roo.isGecko){
58730             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58731                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58732                     "';};};";
58733         }else{
58734             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58735             body.push(tpl.body.replace(/(\r\n|\n)/g,
58736                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58737             body.push("'].join('');};};");
58738             body = body.join('');
58739         }
58740         
58741         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58742        
58743         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58744         eval(body);
58745         
58746         return this;
58747     },
58748
58749     applyTemplate : function(values){
58750         return this.master.compiled.call(this, values, {});
58751         //var s = this.subs;
58752     },
58753
58754     apply : function(){
58755         return this.applyTemplate.apply(this, arguments);
58756     }
58757
58758  });
58759
58760 Roo.XTemplate.from = function(el){
58761     el = Roo.getDom(el);
58762     return new Roo.XTemplate(el.value || el.innerHTML);
58763 };