roojs-bootstrap.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         return function(r){
21703             return value.test(r.data[property]);
21704         };
21705     },
21706
21707     /**
21708      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21709      * @param {String} property A field on your records
21710      * @param {Number} start The record index to start at (defaults to 0)
21711      * @param {Number} end The last record index to include (defaults to length - 1)
21712      * @return {Number} The sum
21713      */
21714     sum : function(property, start, end){
21715         var rs = this.data.items, v = 0;
21716         start = start || 0;
21717         end = (end || end === 0) ? end : rs.length-1;
21718
21719         for(var i = start; i <= end; i++){
21720             v += (rs[i].data[property] || 0);
21721         }
21722         return v;
21723     },
21724
21725     /**
21726      * Filter the records by a specified property.
21727      * @param {String} field A field on your records
21728      * @param {String/RegExp} value Either a string that the field
21729      * should start with or a RegExp to test against the field
21730      * @param {Boolean} anyMatch True to match any part not just the beginning
21731      */
21732     filter : function(property, value, anyMatch){
21733         var fn = this.createFilterFn(property, value, anyMatch);
21734         return fn ? this.filterBy(fn) : this.clearFilter();
21735     },
21736
21737     /**
21738      * Filter by a function. The specified function will be called with each
21739      * record in this data source. If the function returns true the record is included,
21740      * otherwise it is filtered.
21741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21742      * @param {Object} scope (optional) The scope of the function (defaults to this)
21743      */
21744     filterBy : function(fn, scope){
21745         this.snapshot = this.snapshot || this.data;
21746         this.data = this.queryBy(fn, scope||this);
21747         this.fireEvent("datachanged", this);
21748     },
21749
21750     /**
21751      * Query the records by a specified property.
21752      * @param {String} field A field on your records
21753      * @param {String/RegExp} value Either a string that the field
21754      * should start with or a RegExp to test against the field
21755      * @param {Boolean} anyMatch True to match any part not just the beginning
21756      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21757      */
21758     query : function(property, value, anyMatch){
21759         var fn = this.createFilterFn(property, value, anyMatch);
21760         return fn ? this.queryBy(fn) : this.data.clone();
21761     },
21762
21763     /**
21764      * Query by a function. The specified function will be called with each
21765      * record in this data source. If the function returns true the record is included
21766      * in the results.
21767      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21768      * @param {Object} scope (optional) The scope of the function (defaults to this)
21769       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21770      **/
21771     queryBy : function(fn, scope){
21772         var data = this.snapshot || this.data;
21773         return data.filterBy(fn, scope||this);
21774     },
21775
21776     /**
21777      * Collects unique values for a particular dataIndex from this store.
21778      * @param {String} dataIndex The property to collect
21779      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21780      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21781      * @return {Array} An array of the unique values
21782      **/
21783     collect : function(dataIndex, allowNull, bypassFilter){
21784         var d = (bypassFilter === true && this.snapshot) ?
21785                 this.snapshot.items : this.data.items;
21786         var v, sv, r = [], l = {};
21787         for(var i = 0, len = d.length; i < len; i++){
21788             v = d[i].data[dataIndex];
21789             sv = String(v);
21790             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21791                 l[sv] = true;
21792                 r[r.length] = v;
21793             }
21794         }
21795         return r;
21796     },
21797
21798     /**
21799      * Revert to a view of the Record cache with no filtering applied.
21800      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21801      */
21802     clearFilter : function(suppressEvent){
21803         if(this.snapshot && this.snapshot != this.data){
21804             this.data = this.snapshot;
21805             delete this.snapshot;
21806             if(suppressEvent !== true){
21807                 this.fireEvent("datachanged", this);
21808             }
21809         }
21810     },
21811
21812     // private
21813     afterEdit : function(record){
21814         if(this.modified.indexOf(record) == -1){
21815             this.modified.push(record);
21816         }
21817         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21818     },
21819     
21820     // private
21821     afterReject : function(record){
21822         this.modified.remove(record);
21823         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21824     },
21825
21826     // private
21827     afterCommit : function(record){
21828         this.modified.remove(record);
21829         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21830     },
21831
21832     /**
21833      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21834      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21835      */
21836     commitChanges : function(){
21837         var m = this.modified.slice(0);
21838         this.modified = [];
21839         for(var i = 0, len = m.length; i < len; i++){
21840             m[i].commit();
21841         }
21842     },
21843
21844     /**
21845      * Cancel outstanding changes on all changed records.
21846      */
21847     rejectChanges : function(){
21848         var m = this.modified.slice(0);
21849         this.modified = [];
21850         for(var i = 0, len = m.length; i < len; i++){
21851             m[i].reject();
21852         }
21853     },
21854
21855     onMetaChange : function(meta, rtype, o){
21856         this.recordType = rtype;
21857         this.fields = rtype.prototype.fields;
21858         delete this.snapshot;
21859         this.sortInfo = meta.sortInfo || this.sortInfo;
21860         this.modified = [];
21861         this.fireEvent('metachange', this, this.reader.meta);
21862     },
21863     
21864     moveIndex : function(data, type)
21865     {
21866         var index = this.indexOf(data);
21867         
21868         var newIndex = index + type;
21869         
21870         this.remove(data);
21871         
21872         this.insert(newIndex, data);
21873         
21874     }
21875 });/*
21876  * Based on:
21877  * Ext JS Library 1.1.1
21878  * Copyright(c) 2006-2007, Ext JS, LLC.
21879  *
21880  * Originally Released Under LGPL - original licence link has changed is not relivant.
21881  *
21882  * Fork - LGPL
21883  * <script type="text/javascript">
21884  */
21885
21886 /**
21887  * @class Roo.data.SimpleStore
21888  * @extends Roo.data.Store
21889  * Small helper class to make creating Stores from Array data easier.
21890  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21891  * @cfg {Array} fields An array of field definition objects, or field name strings.
21892  * @cfg {Array} data The multi-dimensional array of data
21893  * @constructor
21894  * @param {Object} config
21895  */
21896 Roo.data.SimpleStore = function(config){
21897     Roo.data.SimpleStore.superclass.constructor.call(this, {
21898         isLocal : true,
21899         reader: new Roo.data.ArrayReader({
21900                 id: config.id
21901             },
21902             Roo.data.Record.create(config.fields)
21903         ),
21904         proxy : new Roo.data.MemoryProxy(config.data)
21905     });
21906     this.load();
21907 };
21908 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21909  * Based on:
21910  * Ext JS Library 1.1.1
21911  * Copyright(c) 2006-2007, Ext JS, LLC.
21912  *
21913  * Originally Released Under LGPL - original licence link has changed is not relivant.
21914  *
21915  * Fork - LGPL
21916  * <script type="text/javascript">
21917  */
21918
21919 /**
21920 /**
21921  * @extends Roo.data.Store
21922  * @class Roo.data.JsonStore
21923  * Small helper class to make creating Stores for JSON data easier. <br/>
21924 <pre><code>
21925 var store = new Roo.data.JsonStore({
21926     url: 'get-images.php',
21927     root: 'images',
21928     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21929 });
21930 </code></pre>
21931  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21932  * JsonReader and HttpProxy (unless inline data is provided).</b>
21933  * @cfg {Array} fields An array of field definition objects, or field name strings.
21934  * @constructor
21935  * @param {Object} config
21936  */
21937 Roo.data.JsonStore = function(c){
21938     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21939         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21940         reader: new Roo.data.JsonReader(c, c.fields)
21941     }));
21942 };
21943 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21944  * Based on:
21945  * Ext JS Library 1.1.1
21946  * Copyright(c) 2006-2007, Ext JS, LLC.
21947  *
21948  * Originally Released Under LGPL - original licence link has changed is not relivant.
21949  *
21950  * Fork - LGPL
21951  * <script type="text/javascript">
21952  */
21953
21954  
21955 Roo.data.Field = function(config){
21956     if(typeof config == "string"){
21957         config = {name: config};
21958     }
21959     Roo.apply(this, config);
21960     
21961     if(!this.type){
21962         this.type = "auto";
21963     }
21964     
21965     var st = Roo.data.SortTypes;
21966     // named sortTypes are supported, here we look them up
21967     if(typeof this.sortType == "string"){
21968         this.sortType = st[this.sortType];
21969     }
21970     
21971     // set default sortType for strings and dates
21972     if(!this.sortType){
21973         switch(this.type){
21974             case "string":
21975                 this.sortType = st.asUCString;
21976                 break;
21977             case "date":
21978                 this.sortType = st.asDate;
21979                 break;
21980             default:
21981                 this.sortType = st.none;
21982         }
21983     }
21984
21985     // define once
21986     var stripRe = /[\$,%]/g;
21987
21988     // prebuilt conversion function for this field, instead of
21989     // switching every time we're reading a value
21990     if(!this.convert){
21991         var cv, dateFormat = this.dateFormat;
21992         switch(this.type){
21993             case "":
21994             case "auto":
21995             case undefined:
21996                 cv = function(v){ return v; };
21997                 break;
21998             case "string":
21999                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22000                 break;
22001             case "int":
22002                 cv = function(v){
22003                     return v !== undefined && v !== null && v !== '' ?
22004                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22005                     };
22006                 break;
22007             case "float":
22008                 cv = function(v){
22009                     return v !== undefined && v !== null && v !== '' ?
22010                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22011                     };
22012                 break;
22013             case "bool":
22014             case "boolean":
22015                 cv = function(v){ return v === true || v === "true" || v == 1; };
22016                 break;
22017             case "date":
22018                 cv = function(v){
22019                     if(!v){
22020                         return '';
22021                     }
22022                     if(v instanceof Date){
22023                         return v;
22024                     }
22025                     if(dateFormat){
22026                         if(dateFormat == "timestamp"){
22027                             return new Date(v*1000);
22028                         }
22029                         return Date.parseDate(v, dateFormat);
22030                     }
22031                     var parsed = Date.parse(v);
22032                     return parsed ? new Date(parsed) : null;
22033                 };
22034              break;
22035             
22036         }
22037         this.convert = cv;
22038     }
22039 };
22040
22041 Roo.data.Field.prototype = {
22042     dateFormat: null,
22043     defaultValue: "",
22044     mapping: null,
22045     sortType : null,
22046     sortDir : "ASC"
22047 };/*
22048  * Based on:
22049  * Ext JS Library 1.1.1
22050  * Copyright(c) 2006-2007, Ext JS, LLC.
22051  *
22052  * Originally Released Under LGPL - original licence link has changed is not relivant.
22053  *
22054  * Fork - LGPL
22055  * <script type="text/javascript">
22056  */
22057  
22058 // Base class for reading structured data from a data source.  This class is intended to be
22059 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22060
22061 /**
22062  * @class Roo.data.DataReader
22063  * Base class for reading structured data from a data source.  This class is intended to be
22064  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22065  */
22066
22067 Roo.data.DataReader = function(meta, recordType){
22068     
22069     this.meta = meta;
22070     
22071     this.recordType = recordType instanceof Array ? 
22072         Roo.data.Record.create(recordType) : recordType;
22073 };
22074
22075 Roo.data.DataReader.prototype = {
22076      /**
22077      * Create an empty record
22078      * @param {Object} data (optional) - overlay some values
22079      * @return {Roo.data.Record} record created.
22080      */
22081     newRow :  function(d) {
22082         var da =  {};
22083         this.recordType.prototype.fields.each(function(c) {
22084             switch( c.type) {
22085                 case 'int' : da[c.name] = 0; break;
22086                 case 'date' : da[c.name] = new Date(); break;
22087                 case 'float' : da[c.name] = 0.0; break;
22088                 case 'boolean' : da[c.name] = false; break;
22089                 default : da[c.name] = ""; break;
22090             }
22091             
22092         });
22093         return new this.recordType(Roo.apply(da, d));
22094     }
22095     
22096 };/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106
22107 /**
22108  * @class Roo.data.DataProxy
22109  * @extends Roo.data.Observable
22110  * This class is an abstract base class for implementations which provide retrieval of
22111  * unformatted data objects.<br>
22112  * <p>
22113  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22114  * (of the appropriate type which knows how to parse the data object) to provide a block of
22115  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22116  * <p>
22117  * Custom implementations must implement the load method as described in
22118  * {@link Roo.data.HttpProxy#load}.
22119  */
22120 Roo.data.DataProxy = function(){
22121     this.addEvents({
22122         /**
22123          * @event beforeload
22124          * Fires before a network request is made to retrieve a data object.
22125          * @param {Object} This DataProxy object.
22126          * @param {Object} params The params parameter to the load function.
22127          */
22128         beforeload : true,
22129         /**
22130          * @event load
22131          * Fires before the load method's callback is called.
22132          * @param {Object} This DataProxy object.
22133          * @param {Object} o The data object.
22134          * @param {Object} arg The callback argument object passed to the load function.
22135          */
22136         load : true,
22137         /**
22138          * @event loadexception
22139          * Fires if an Exception occurs during data retrieval.
22140          * @param {Object} This DataProxy object.
22141          * @param {Object} o The data object.
22142          * @param {Object} arg The callback argument object passed to the load function.
22143          * @param {Object} e The Exception.
22144          */
22145         loadexception : true
22146     });
22147     Roo.data.DataProxy.superclass.constructor.call(this);
22148 };
22149
22150 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22151
22152     /**
22153      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22154      */
22155 /*
22156  * Based on:
22157  * Ext JS Library 1.1.1
22158  * Copyright(c) 2006-2007, Ext JS, LLC.
22159  *
22160  * Originally Released Under LGPL - original licence link has changed is not relivant.
22161  *
22162  * Fork - LGPL
22163  * <script type="text/javascript">
22164  */
22165 /**
22166  * @class Roo.data.MemoryProxy
22167  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22168  * to the Reader when its load method is called.
22169  * @constructor
22170  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22171  */
22172 Roo.data.MemoryProxy = function(data){
22173     if (data.data) {
22174         data = data.data;
22175     }
22176     Roo.data.MemoryProxy.superclass.constructor.call(this);
22177     this.data = data;
22178 };
22179
22180 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22181     /**
22182      * Load data from the requested source (in this case an in-memory
22183      * data object passed to the constructor), read the data object into
22184      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22185      * process that block using the passed callback.
22186      * @param {Object} params This parameter is not used by the MemoryProxy class.
22187      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22188      * object into a block of Roo.data.Records.
22189      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22190      * The function must be passed <ul>
22191      * <li>The Record block object</li>
22192      * <li>The "arg" argument from the load function</li>
22193      * <li>A boolean success indicator</li>
22194      * </ul>
22195      * @param {Object} scope The scope in which to call the callback
22196      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22197      */
22198     load : function(params, reader, callback, scope, arg){
22199         params = params || {};
22200         var result;
22201         try {
22202             result = reader.readRecords(this.data);
22203         }catch(e){
22204             this.fireEvent("loadexception", this, arg, null, e);
22205             callback.call(scope, null, arg, false);
22206             return;
22207         }
22208         callback.call(scope, result, arg, true);
22209     },
22210     
22211     // private
22212     update : function(params, records){
22213         
22214     }
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225 /**
22226  * @class Roo.data.HttpProxy
22227  * @extends Roo.data.DataProxy
22228  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22229  * configured to reference a certain URL.<br><br>
22230  * <p>
22231  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22232  * from which the running page was served.<br><br>
22233  * <p>
22234  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22235  * <p>
22236  * Be aware that to enable the browser to parse an XML document, the server must set
22237  * the Content-Type header in the HTTP response to "text/xml".
22238  * @constructor
22239  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22240  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22241  * will be used to make the request.
22242  */
22243 Roo.data.HttpProxy = function(conn){
22244     Roo.data.HttpProxy.superclass.constructor.call(this);
22245     // is conn a conn config or a real conn?
22246     this.conn = conn;
22247     this.useAjax = !conn || !conn.events;
22248   
22249 };
22250
22251 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22252     // thse are take from connection...
22253     
22254     /**
22255      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22256      */
22257     /**
22258      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22259      * extra parameters to each request made by this object. (defaults to undefined)
22260      */
22261     /**
22262      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22263      *  to each request made by this object. (defaults to undefined)
22264      */
22265     /**
22266      * @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)
22267      */
22268     /**
22269      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22270      */
22271      /**
22272      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22273      * @type Boolean
22274      */
22275   
22276
22277     /**
22278      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22279      * @type Boolean
22280      */
22281     /**
22282      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22283      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22284      * a finer-grained basis than the DataProxy events.
22285      */
22286     getConnection : function(){
22287         return this.useAjax ? Roo.Ajax : this.conn;
22288     },
22289
22290     /**
22291      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22292      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22293      * process that block using the passed callback.
22294      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22295      * for the request to the remote server.
22296      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22297      * object into a block of Roo.data.Records.
22298      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22299      * The function must be passed <ul>
22300      * <li>The Record block object</li>
22301      * <li>The "arg" argument from the load function</li>
22302      * <li>A boolean success indicator</li>
22303      * </ul>
22304      * @param {Object} scope The scope in which to call the callback
22305      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22306      */
22307     load : function(params, reader, callback, scope, arg){
22308         if(this.fireEvent("beforeload", this, params) !== false){
22309             var  o = {
22310                 params : params || {},
22311                 request: {
22312                     callback : callback,
22313                     scope : scope,
22314                     arg : arg
22315                 },
22316                 reader: reader,
22317                 callback : this.loadResponse,
22318                 scope: this
22319             };
22320             if(this.useAjax){
22321                 Roo.applyIf(o, this.conn);
22322                 if(this.activeRequest){
22323                     Roo.Ajax.abort(this.activeRequest);
22324                 }
22325                 this.activeRequest = Roo.Ajax.request(o);
22326             }else{
22327                 this.conn.request(o);
22328             }
22329         }else{
22330             callback.call(scope||this, null, arg, false);
22331         }
22332     },
22333
22334     // private
22335     loadResponse : function(o, success, response){
22336         delete this.activeRequest;
22337         if(!success){
22338             this.fireEvent("loadexception", this, o, response);
22339             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22340             return;
22341         }
22342         var result;
22343         try {
22344             result = o.reader.read(response);
22345         }catch(e){
22346             this.fireEvent("loadexception", this, o, response, e);
22347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22348             return;
22349         }
22350         
22351         this.fireEvent("load", this, o, o.request.arg);
22352         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22353     },
22354
22355     // private
22356     update : function(dataSet){
22357
22358     },
22359
22360     // private
22361     updateResponse : function(dataSet){
22362
22363     }
22364 });/*
22365  * Based on:
22366  * Ext JS Library 1.1.1
22367  * Copyright(c) 2006-2007, Ext JS, LLC.
22368  *
22369  * Originally Released Under LGPL - original licence link has changed is not relivant.
22370  *
22371  * Fork - LGPL
22372  * <script type="text/javascript">
22373  */
22374
22375 /**
22376  * @class Roo.data.ScriptTagProxy
22377  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22378  * other than the originating domain of the running page.<br><br>
22379  * <p>
22380  * <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
22381  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22382  * <p>
22383  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22384  * source code that is used as the source inside a &lt;script> tag.<br><br>
22385  * <p>
22386  * In order for the browser to process the returned data, the server must wrap the data object
22387  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22388  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22389  * depending on whether the callback name was passed:
22390  * <p>
22391  * <pre><code>
22392 boolean scriptTag = false;
22393 String cb = request.getParameter("callback");
22394 if (cb != null) {
22395     scriptTag = true;
22396     response.setContentType("text/javascript");
22397 } else {
22398     response.setContentType("application/x-json");
22399 }
22400 Writer out = response.getWriter();
22401 if (scriptTag) {
22402     out.write(cb + "(");
22403 }
22404 out.print(dataBlock.toJsonString());
22405 if (scriptTag) {
22406     out.write(");");
22407 }
22408 </pre></code>
22409  *
22410  * @constructor
22411  * @param {Object} config A configuration object.
22412  */
22413 Roo.data.ScriptTagProxy = function(config){
22414     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22415     Roo.apply(this, config);
22416     this.head = document.getElementsByTagName("head")[0];
22417 };
22418
22419 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22420
22421 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22422     /**
22423      * @cfg {String} url The URL from which to request the data object.
22424      */
22425     /**
22426      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22427      */
22428     timeout : 30000,
22429     /**
22430      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22431      * the server the name of the callback function set up by the load call to process the returned data object.
22432      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22433      * javascript output which calls this named function passing the data object as its only parameter.
22434      */
22435     callbackParam : "callback",
22436     /**
22437      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22438      * name to the request.
22439      */
22440     nocache : true,
22441
22442     /**
22443      * Load data from the configured URL, read the data object into
22444      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22445      * process that block using the passed callback.
22446      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22447      * for the request to the remote server.
22448      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22449      * object into a block of Roo.data.Records.
22450      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22451      * The function must be passed <ul>
22452      * <li>The Record block object</li>
22453      * <li>The "arg" argument from the load function</li>
22454      * <li>A boolean success indicator</li>
22455      * </ul>
22456      * @param {Object} scope The scope in which to call the callback
22457      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22458      */
22459     load : function(params, reader, callback, scope, arg){
22460         if(this.fireEvent("beforeload", this, params) !== false){
22461
22462             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22463
22464             var url = this.url;
22465             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22466             if(this.nocache){
22467                 url += "&_dc=" + (new Date().getTime());
22468             }
22469             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22470             var trans = {
22471                 id : transId,
22472                 cb : "stcCallback"+transId,
22473                 scriptId : "stcScript"+transId,
22474                 params : params,
22475                 arg : arg,
22476                 url : url,
22477                 callback : callback,
22478                 scope : scope,
22479                 reader : reader
22480             };
22481             var conn = this;
22482
22483             window[trans.cb] = function(o){
22484                 conn.handleResponse(o, trans);
22485             };
22486
22487             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22488
22489             if(this.autoAbort !== false){
22490                 this.abort();
22491             }
22492
22493             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22494
22495             var script = document.createElement("script");
22496             script.setAttribute("src", url);
22497             script.setAttribute("type", "text/javascript");
22498             script.setAttribute("id", trans.scriptId);
22499             this.head.appendChild(script);
22500
22501             this.trans = trans;
22502         }else{
22503             callback.call(scope||this, null, arg, false);
22504         }
22505     },
22506
22507     // private
22508     isLoading : function(){
22509         return this.trans ? true : false;
22510     },
22511
22512     /**
22513      * Abort the current server request.
22514      */
22515     abort : function(){
22516         if(this.isLoading()){
22517             this.destroyTrans(this.trans);
22518         }
22519     },
22520
22521     // private
22522     destroyTrans : function(trans, isLoaded){
22523         this.head.removeChild(document.getElementById(trans.scriptId));
22524         clearTimeout(trans.timeoutId);
22525         if(isLoaded){
22526             window[trans.cb] = undefined;
22527             try{
22528                 delete window[trans.cb];
22529             }catch(e){}
22530         }else{
22531             // if hasn't been loaded, wait for load to remove it to prevent script error
22532             window[trans.cb] = function(){
22533                 window[trans.cb] = undefined;
22534                 try{
22535                     delete window[trans.cb];
22536                 }catch(e){}
22537             };
22538         }
22539     },
22540
22541     // private
22542     handleResponse : function(o, trans){
22543         this.trans = false;
22544         this.destroyTrans(trans, true);
22545         var result;
22546         try {
22547             result = trans.reader.readRecords(o);
22548         }catch(e){
22549             this.fireEvent("loadexception", this, o, trans.arg, e);
22550             trans.callback.call(trans.scope||window, null, trans.arg, false);
22551             return;
22552         }
22553         this.fireEvent("load", this, o, trans.arg);
22554         trans.callback.call(trans.scope||window, result, trans.arg, true);
22555     },
22556
22557     // private
22558     handleFailure : function(trans){
22559         this.trans = false;
22560         this.destroyTrans(trans, false);
22561         this.fireEvent("loadexception", this, null, trans.arg);
22562         trans.callback.call(trans.scope||window, null, trans.arg, false);
22563     }
22564 });/*
22565  * Based on:
22566  * Ext JS Library 1.1.1
22567  * Copyright(c) 2006-2007, Ext JS, LLC.
22568  *
22569  * Originally Released Under LGPL - original licence link has changed is not relivant.
22570  *
22571  * Fork - LGPL
22572  * <script type="text/javascript">
22573  */
22574
22575 /**
22576  * @class Roo.data.JsonReader
22577  * @extends Roo.data.DataReader
22578  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22579  * based on mappings in a provided Roo.data.Record constructor.
22580  * 
22581  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22582  * in the reply previously. 
22583  * 
22584  * <p>
22585  * Example code:
22586  * <pre><code>
22587 var RecordDef = Roo.data.Record.create([
22588     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22589     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22590 ]);
22591 var myReader = new Roo.data.JsonReader({
22592     totalProperty: "results",    // The property which contains the total dataset size (optional)
22593     root: "rows",                // The property which contains an Array of row objects
22594     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22595 }, RecordDef);
22596 </code></pre>
22597  * <p>
22598  * This would consume a JSON file like this:
22599  * <pre><code>
22600 { 'results': 2, 'rows': [
22601     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22602     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22603 }
22604 </code></pre>
22605  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22606  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22607  * paged from the remote server.
22608  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22609  * @cfg {String} root name of the property which contains the Array of row objects.
22610  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22611  * @constructor
22612  * Create a new JsonReader
22613  * @param {Object} meta Metadata configuration options
22614  * @param {Object} recordType Either an Array of field definition objects,
22615  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22616  */
22617 Roo.data.JsonReader = function(meta, recordType){
22618     
22619     meta = meta || {};
22620     // set some defaults:
22621     Roo.applyIf(meta, {
22622         totalProperty: 'total',
22623         successProperty : 'success',
22624         root : 'data',
22625         id : 'id'
22626     });
22627     
22628     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22629 };
22630 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22631     
22632     /**
22633      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22634      * Used by Store query builder to append _requestMeta to params.
22635      * 
22636      */
22637     metaFromRemote : false,
22638     /**
22639      * This method is only used by a DataProxy which has retrieved data from a remote server.
22640      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22641      * @return {Object} data A data block which is used by an Roo.data.Store object as
22642      * a cache of Roo.data.Records.
22643      */
22644     read : function(response){
22645         var json = response.responseText;
22646        
22647         var o = /* eval:var:o */ eval("("+json+")");
22648         if(!o) {
22649             throw {message: "JsonReader.read: Json object not found"};
22650         }
22651         
22652         if(o.metaData){
22653             
22654             delete this.ef;
22655             this.metaFromRemote = true;
22656             this.meta = o.metaData;
22657             this.recordType = Roo.data.Record.create(o.metaData.fields);
22658             this.onMetaChange(this.meta, this.recordType, o);
22659         }
22660         return this.readRecords(o);
22661     },
22662
22663     // private function a store will implement
22664     onMetaChange : function(meta, recordType, o){
22665
22666     },
22667
22668     /**
22669          * @ignore
22670          */
22671     simpleAccess: function(obj, subsc) {
22672         return obj[subsc];
22673     },
22674
22675         /**
22676          * @ignore
22677          */
22678     getJsonAccessor: function(){
22679         var re = /[\[\.]/;
22680         return function(expr) {
22681             try {
22682                 return(re.test(expr))
22683                     ? new Function("obj", "return obj." + expr)
22684                     : function(obj){
22685                         return obj[expr];
22686                     };
22687             } catch(e){}
22688             return Roo.emptyFn;
22689         };
22690     }(),
22691
22692     /**
22693      * Create a data block containing Roo.data.Records from an XML document.
22694      * @param {Object} o An object which contains an Array of row objects in the property specified
22695      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22696      * which contains the total size of the dataset.
22697      * @return {Object} data A data block which is used by an Roo.data.Store object as
22698      * a cache of Roo.data.Records.
22699      */
22700     readRecords : function(o){
22701         /**
22702          * After any data loads, the raw JSON data is available for further custom processing.
22703          * @type Object
22704          */
22705         this.o = o;
22706         var s = this.meta, Record = this.recordType,
22707             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22708
22709 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22710         if (!this.ef) {
22711             if(s.totalProperty) {
22712                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22713                 }
22714                 if(s.successProperty) {
22715                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22716                 }
22717                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22718                 if (s.id) {
22719                         var g = this.getJsonAccessor(s.id);
22720                         this.getId = function(rec) {
22721                                 var r = g(rec);  
22722                                 return (r === undefined || r === "") ? null : r;
22723                         };
22724                 } else {
22725                         this.getId = function(){return null;};
22726                 }
22727             this.ef = [];
22728             for(var jj = 0; jj < fl; jj++){
22729                 f = fi[jj];
22730                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22731                 this.ef[jj] = this.getJsonAccessor(map);
22732             }
22733         }
22734
22735         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22736         if(s.totalProperty){
22737             var vt = parseInt(this.getTotal(o), 10);
22738             if(!isNaN(vt)){
22739                 totalRecords = vt;
22740             }
22741         }
22742         if(s.successProperty){
22743             var vs = this.getSuccess(o);
22744             if(vs === false || vs === 'false'){
22745                 success = false;
22746             }
22747         }
22748         var records = [];
22749         for(var i = 0; i < c; i++){
22750                 var n = root[i];
22751             var values = {};
22752             var id = this.getId(n);
22753             for(var j = 0; j < fl; j++){
22754                 f = fi[j];
22755             var v = this.ef[j](n);
22756             if (!f.convert) {
22757                 Roo.log('missing convert for ' + f.name);
22758                 Roo.log(f);
22759                 continue;
22760             }
22761             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22762             }
22763             var record = new Record(values, id);
22764             record.json = n;
22765             records[i] = record;
22766         }
22767         return {
22768             raw : o,
22769             success : success,
22770             records : records,
22771             totalRecords : totalRecords
22772         };
22773     }
22774 });/*
22775  * Based on:
22776  * Ext JS Library 1.1.1
22777  * Copyright(c) 2006-2007, Ext JS, LLC.
22778  *
22779  * Originally Released Under LGPL - original licence link has changed is not relivant.
22780  *
22781  * Fork - LGPL
22782  * <script type="text/javascript">
22783  */
22784
22785 /**
22786  * @class Roo.data.XmlReader
22787  * @extends Roo.data.DataReader
22788  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22789  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22790  * <p>
22791  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22792  * header in the HTTP response must be set to "text/xml".</em>
22793  * <p>
22794  * Example code:
22795  * <pre><code>
22796 var RecordDef = Roo.data.Record.create([
22797    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22798    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22799 ]);
22800 var myReader = new Roo.data.XmlReader({
22801    totalRecords: "results", // The element which contains the total dataset size (optional)
22802    record: "row",           // The repeated element which contains row information
22803    id: "id"                 // The element within the row that provides an ID for the record (optional)
22804 }, RecordDef);
22805 </code></pre>
22806  * <p>
22807  * This would consume an XML file like this:
22808  * <pre><code>
22809 &lt;?xml?>
22810 &lt;dataset>
22811  &lt;results>2&lt;/results>
22812  &lt;row>
22813    &lt;id>1&lt;/id>
22814    &lt;name>Bill&lt;/name>
22815    &lt;occupation>Gardener&lt;/occupation>
22816  &lt;/row>
22817  &lt;row>
22818    &lt;id>2&lt;/id>
22819    &lt;name>Ben&lt;/name>
22820    &lt;occupation>Horticulturalist&lt;/occupation>
22821  &lt;/row>
22822 &lt;/dataset>
22823 </code></pre>
22824  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22825  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22826  * paged from the remote server.
22827  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22828  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22829  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22830  * a record identifier value.
22831  * @constructor
22832  * Create a new XmlReader
22833  * @param {Object} meta Metadata configuration options
22834  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22835  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22836  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22837  */
22838 Roo.data.XmlReader = function(meta, recordType){
22839     meta = meta || {};
22840     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22841 };
22842 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22843     /**
22844      * This method is only used by a DataProxy which has retrieved data from a remote server.
22845          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22846          * to contain a method called 'responseXML' that returns an XML document object.
22847      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22848      * a cache of Roo.data.Records.
22849      */
22850     read : function(response){
22851         var doc = response.responseXML;
22852         if(!doc) {
22853             throw {message: "XmlReader.read: XML Document not available"};
22854         }
22855         return this.readRecords(doc);
22856     },
22857
22858     /**
22859      * Create a data block containing Roo.data.Records from an XML document.
22860          * @param {Object} doc A parsed XML document.
22861      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22862      * a cache of Roo.data.Records.
22863      */
22864     readRecords : function(doc){
22865         /**
22866          * After any data loads/reads, the raw XML Document is available for further custom processing.
22867          * @type XMLDocument
22868          */
22869         this.xmlData = doc;
22870         var root = doc.documentElement || doc;
22871         var q = Roo.DomQuery;
22872         var recordType = this.recordType, fields = recordType.prototype.fields;
22873         var sid = this.meta.id;
22874         var totalRecords = 0, success = true;
22875         if(this.meta.totalRecords){
22876             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22877         }
22878         
22879         if(this.meta.success){
22880             var sv = q.selectValue(this.meta.success, root, true);
22881             success = sv !== false && sv !== 'false';
22882         }
22883         var records = [];
22884         var ns = q.select(this.meta.record, root);
22885         for(var i = 0, len = ns.length; i < len; i++) {
22886                 var n = ns[i];
22887                 var values = {};
22888                 var id = sid ? q.selectValue(sid, n) : undefined;
22889                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22890                     var f = fields.items[j];
22891                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22892                     v = f.convert(v);
22893                     values[f.name] = v;
22894                 }
22895                 var record = new recordType(values, id);
22896                 record.node = n;
22897                 records[records.length] = record;
22898             }
22899
22900             return {
22901                 success : success,
22902                 records : records,
22903                 totalRecords : totalRecords || records.length
22904             };
22905     }
22906 });/*
22907  * Based on:
22908  * Ext JS Library 1.1.1
22909  * Copyright(c) 2006-2007, Ext JS, LLC.
22910  *
22911  * Originally Released Under LGPL - original licence link has changed is not relivant.
22912  *
22913  * Fork - LGPL
22914  * <script type="text/javascript">
22915  */
22916
22917 /**
22918  * @class Roo.data.ArrayReader
22919  * @extends Roo.data.DataReader
22920  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22921  * Each element of that Array represents a row of data fields. The
22922  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22923  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22924  * <p>
22925  * Example code:.
22926  * <pre><code>
22927 var RecordDef = Roo.data.Record.create([
22928     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22929     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22930 ]);
22931 var myReader = new Roo.data.ArrayReader({
22932     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22933 }, RecordDef);
22934 </code></pre>
22935  * <p>
22936  * This would consume an Array like this:
22937  * <pre><code>
22938 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22939   </code></pre>
22940  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22941  * @constructor
22942  * Create a new JsonReader
22943  * @param {Object} meta Metadata configuration options.
22944  * @param {Object} recordType Either an Array of field definition objects
22945  * as specified to {@link Roo.data.Record#create},
22946  * or an {@link Roo.data.Record} object
22947  * created using {@link Roo.data.Record#create}.
22948  */
22949 Roo.data.ArrayReader = function(meta, recordType){
22950     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22951 };
22952
22953 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22954     /**
22955      * Create a data block containing Roo.data.Records from an XML document.
22956      * @param {Object} o An Array of row objects which represents the dataset.
22957      * @return {Object} data A data block which is used by an Roo.data.Store object as
22958      * a cache of Roo.data.Records.
22959      */
22960     readRecords : function(o){
22961         var sid = this.meta ? this.meta.id : null;
22962         var recordType = this.recordType, fields = recordType.prototype.fields;
22963         var records = [];
22964         var root = o;
22965             for(var i = 0; i < root.length; i++){
22966                     var n = root[i];
22967                 var values = {};
22968                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22969                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22970                 var f = fields.items[j];
22971                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22972                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22973                 v = f.convert(v);
22974                 values[f.name] = v;
22975             }
22976                 var record = new recordType(values, id);
22977                 record.json = n;
22978                 records[records.length] = record;
22979             }
22980             return {
22981                 records : records,
22982                 totalRecords : records.length
22983             };
22984     }
22985 });/*
22986  * Based on:
22987  * Ext JS Library 1.1.1
22988  * Copyright(c) 2006-2007, Ext JS, LLC.
22989  *
22990  * Originally Released Under LGPL - original licence link has changed is not relivant.
22991  *
22992  * Fork - LGPL
22993  * <script type="text/javascript">
22994  */
22995
22996
22997 /**
22998  * @class Roo.data.Tree
22999  * @extends Roo.util.Observable
23000  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23001  * in the tree have most standard DOM functionality.
23002  * @constructor
23003  * @param {Node} root (optional) The root node
23004  */
23005 Roo.data.Tree = function(root){
23006    this.nodeHash = {};
23007    /**
23008     * The root node for this tree
23009     * @type Node
23010     */
23011    this.root = null;
23012    if(root){
23013        this.setRootNode(root);
23014    }
23015    this.addEvents({
23016        /**
23017         * @event append
23018         * Fires when a new child node is appended to a node in this tree.
23019         * @param {Tree} tree The owner tree
23020         * @param {Node} parent The parent node
23021         * @param {Node} node The newly appended node
23022         * @param {Number} index The index of the newly appended node
23023         */
23024        "append" : true,
23025        /**
23026         * @event remove
23027         * Fires when a child node is removed from a node in this tree.
23028         * @param {Tree} tree The owner tree
23029         * @param {Node} parent The parent node
23030         * @param {Node} node The child node removed
23031         */
23032        "remove" : true,
23033        /**
23034         * @event move
23035         * Fires when a node is moved to a new location in the tree
23036         * @param {Tree} tree The owner tree
23037         * @param {Node} node The node moved
23038         * @param {Node} oldParent The old parent of this node
23039         * @param {Node} newParent The new parent of this node
23040         * @param {Number} index The index it was moved to
23041         */
23042        "move" : true,
23043        /**
23044         * @event insert
23045         * Fires when a new child node is inserted in a node in this tree.
23046         * @param {Tree} tree The owner tree
23047         * @param {Node} parent The parent node
23048         * @param {Node} node The child node inserted
23049         * @param {Node} refNode The child node the node was inserted before
23050         */
23051        "insert" : true,
23052        /**
23053         * @event beforeappend
23054         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23055         * @param {Tree} tree The owner tree
23056         * @param {Node} parent The parent node
23057         * @param {Node} node The child node to be appended
23058         */
23059        "beforeappend" : true,
23060        /**
23061         * @event beforeremove
23062         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23063         * @param {Tree} tree The owner tree
23064         * @param {Node} parent The parent node
23065         * @param {Node} node The child node to be removed
23066         */
23067        "beforeremove" : true,
23068        /**
23069         * @event beforemove
23070         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23071         * @param {Tree} tree The owner tree
23072         * @param {Node} node The node being moved
23073         * @param {Node} oldParent The parent of the node
23074         * @param {Node} newParent The new parent the node is moving to
23075         * @param {Number} index The index it is being moved to
23076         */
23077        "beforemove" : true,
23078        /**
23079         * @event beforeinsert
23080         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23081         * @param {Tree} tree The owner tree
23082         * @param {Node} parent The parent node
23083         * @param {Node} node The child node to be inserted
23084         * @param {Node} refNode The child node the node is being inserted before
23085         */
23086        "beforeinsert" : true
23087    });
23088
23089     Roo.data.Tree.superclass.constructor.call(this);
23090 };
23091
23092 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23093     pathSeparator: "/",
23094
23095     proxyNodeEvent : function(){
23096         return this.fireEvent.apply(this, arguments);
23097     },
23098
23099     /**
23100      * Returns the root node for this tree.
23101      * @return {Node}
23102      */
23103     getRootNode : function(){
23104         return this.root;
23105     },
23106
23107     /**
23108      * Sets the root node for this tree.
23109      * @param {Node} node
23110      * @return {Node}
23111      */
23112     setRootNode : function(node){
23113         this.root = node;
23114         node.ownerTree = this;
23115         node.isRoot = true;
23116         this.registerNode(node);
23117         return node;
23118     },
23119
23120     /**
23121      * Gets a node in this tree by its id.
23122      * @param {String} id
23123      * @return {Node}
23124      */
23125     getNodeById : function(id){
23126         return this.nodeHash[id];
23127     },
23128
23129     registerNode : function(node){
23130         this.nodeHash[node.id] = node;
23131     },
23132
23133     unregisterNode : function(node){
23134         delete this.nodeHash[node.id];
23135     },
23136
23137     toString : function(){
23138         return "[Tree"+(this.id?" "+this.id:"")+"]";
23139     }
23140 });
23141
23142 /**
23143  * @class Roo.data.Node
23144  * @extends Roo.util.Observable
23145  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23146  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23147  * @constructor
23148  * @param {Object} attributes The attributes/config for the node
23149  */
23150 Roo.data.Node = function(attributes){
23151     /**
23152      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23153      * @type {Object}
23154      */
23155     this.attributes = attributes || {};
23156     this.leaf = this.attributes.leaf;
23157     /**
23158      * The node id. @type String
23159      */
23160     this.id = this.attributes.id;
23161     if(!this.id){
23162         this.id = Roo.id(null, "ynode-");
23163         this.attributes.id = this.id;
23164     }
23165      
23166     
23167     /**
23168      * All child nodes of this node. @type Array
23169      */
23170     this.childNodes = [];
23171     if(!this.childNodes.indexOf){ // indexOf is a must
23172         this.childNodes.indexOf = function(o){
23173             for(var i = 0, len = this.length; i < len; i++){
23174                 if(this[i] == o) {
23175                     return i;
23176                 }
23177             }
23178             return -1;
23179         };
23180     }
23181     /**
23182      * The parent node for this node. @type Node
23183      */
23184     this.parentNode = null;
23185     /**
23186      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23187      */
23188     this.firstChild = null;
23189     /**
23190      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23191      */
23192     this.lastChild = null;
23193     /**
23194      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23195      */
23196     this.previousSibling = null;
23197     /**
23198      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23199      */
23200     this.nextSibling = null;
23201
23202     this.addEvents({
23203        /**
23204         * @event append
23205         * Fires when a new child node is appended
23206         * @param {Tree} tree The owner tree
23207         * @param {Node} this This node
23208         * @param {Node} node The newly appended node
23209         * @param {Number} index The index of the newly appended node
23210         */
23211        "append" : true,
23212        /**
23213         * @event remove
23214         * Fires when a child node is removed
23215         * @param {Tree} tree The owner tree
23216         * @param {Node} this This node
23217         * @param {Node} node The removed node
23218         */
23219        "remove" : true,
23220        /**
23221         * @event move
23222         * Fires when this node is moved to a new location in the tree
23223         * @param {Tree} tree The owner tree
23224         * @param {Node} this This node
23225         * @param {Node} oldParent The old parent of this node
23226         * @param {Node} newParent The new parent of this node
23227         * @param {Number} index The index it was moved to
23228         */
23229        "move" : true,
23230        /**
23231         * @event insert
23232         * Fires when a new child node is inserted.
23233         * @param {Tree} tree The owner tree
23234         * @param {Node} this This node
23235         * @param {Node} node The child node inserted
23236         * @param {Node} refNode The child node the node was inserted before
23237         */
23238        "insert" : true,
23239        /**
23240         * @event beforeappend
23241         * Fires before a new child is appended, return false to cancel the append.
23242         * @param {Tree} tree The owner tree
23243         * @param {Node} this This node
23244         * @param {Node} node The child node to be appended
23245         */
23246        "beforeappend" : true,
23247        /**
23248         * @event beforeremove
23249         * Fires before a child is removed, return false to cancel the remove.
23250         * @param {Tree} tree The owner tree
23251         * @param {Node} this This node
23252         * @param {Node} node The child node to be removed
23253         */
23254        "beforeremove" : true,
23255        /**
23256         * @event beforemove
23257         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23258         * @param {Tree} tree The owner tree
23259         * @param {Node} this This node
23260         * @param {Node} oldParent The parent of this node
23261         * @param {Node} newParent The new parent this node is moving to
23262         * @param {Number} index The index it is being moved to
23263         */
23264        "beforemove" : true,
23265        /**
23266         * @event beforeinsert
23267         * Fires before a new child is inserted, return false to cancel the insert.
23268         * @param {Tree} tree The owner tree
23269         * @param {Node} this This node
23270         * @param {Node} node The child node to be inserted
23271         * @param {Node} refNode The child node the node is being inserted before
23272         */
23273        "beforeinsert" : true
23274    });
23275     this.listeners = this.attributes.listeners;
23276     Roo.data.Node.superclass.constructor.call(this);
23277 };
23278
23279 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23280     fireEvent : function(evtName){
23281         // first do standard event for this node
23282         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23283             return false;
23284         }
23285         // then bubble it up to the tree if the event wasn't cancelled
23286         var ot = this.getOwnerTree();
23287         if(ot){
23288             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23289                 return false;
23290             }
23291         }
23292         return true;
23293     },
23294
23295     /**
23296      * Returns true if this node is a leaf
23297      * @return {Boolean}
23298      */
23299     isLeaf : function(){
23300         return this.leaf === true;
23301     },
23302
23303     // private
23304     setFirstChild : function(node){
23305         this.firstChild = node;
23306     },
23307
23308     //private
23309     setLastChild : function(node){
23310         this.lastChild = node;
23311     },
23312
23313
23314     /**
23315      * Returns true if this node is the last child of its parent
23316      * @return {Boolean}
23317      */
23318     isLast : function(){
23319        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23320     },
23321
23322     /**
23323      * Returns true if this node is the first child of its parent
23324      * @return {Boolean}
23325      */
23326     isFirst : function(){
23327        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23328     },
23329
23330     hasChildNodes : function(){
23331         return !this.isLeaf() && this.childNodes.length > 0;
23332     },
23333
23334     /**
23335      * Insert node(s) as the last child node of this node.
23336      * @param {Node/Array} node The node or Array of nodes to append
23337      * @return {Node} The appended node if single append, or null if an array was passed
23338      */
23339     appendChild : function(node){
23340         var multi = false;
23341         if(node instanceof Array){
23342             multi = node;
23343         }else if(arguments.length > 1){
23344             multi = arguments;
23345         }
23346         // if passed an array or multiple args do them one by one
23347         if(multi){
23348             for(var i = 0, len = multi.length; i < len; i++) {
23349                 this.appendChild(multi[i]);
23350             }
23351         }else{
23352             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23353                 return false;
23354             }
23355             var index = this.childNodes.length;
23356             var oldParent = node.parentNode;
23357             // it's a move, make sure we move it cleanly
23358             if(oldParent){
23359                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23360                     return false;
23361                 }
23362                 oldParent.removeChild(node);
23363             }
23364             index = this.childNodes.length;
23365             if(index == 0){
23366                 this.setFirstChild(node);
23367             }
23368             this.childNodes.push(node);
23369             node.parentNode = this;
23370             var ps = this.childNodes[index-1];
23371             if(ps){
23372                 node.previousSibling = ps;
23373                 ps.nextSibling = node;
23374             }else{
23375                 node.previousSibling = null;
23376             }
23377             node.nextSibling = null;
23378             this.setLastChild(node);
23379             node.setOwnerTree(this.getOwnerTree());
23380             this.fireEvent("append", this.ownerTree, this, node, index);
23381             if(oldParent){
23382                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23383             }
23384             return node;
23385         }
23386     },
23387
23388     /**
23389      * Removes a child node from this node.
23390      * @param {Node} node The node to remove
23391      * @return {Node} The removed node
23392      */
23393     removeChild : function(node){
23394         var index = this.childNodes.indexOf(node);
23395         if(index == -1){
23396             return false;
23397         }
23398         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23399             return false;
23400         }
23401
23402         // remove it from childNodes collection
23403         this.childNodes.splice(index, 1);
23404
23405         // update siblings
23406         if(node.previousSibling){
23407             node.previousSibling.nextSibling = node.nextSibling;
23408         }
23409         if(node.nextSibling){
23410             node.nextSibling.previousSibling = node.previousSibling;
23411         }
23412
23413         // update child refs
23414         if(this.firstChild == node){
23415             this.setFirstChild(node.nextSibling);
23416         }
23417         if(this.lastChild == node){
23418             this.setLastChild(node.previousSibling);
23419         }
23420
23421         node.setOwnerTree(null);
23422         // clear any references from the node
23423         node.parentNode = null;
23424         node.previousSibling = null;
23425         node.nextSibling = null;
23426         this.fireEvent("remove", this.ownerTree, this, node);
23427         return node;
23428     },
23429
23430     /**
23431      * Inserts the first node before the second node in this nodes childNodes collection.
23432      * @param {Node} node The node to insert
23433      * @param {Node} refNode The node to insert before (if null the node is appended)
23434      * @return {Node} The inserted node
23435      */
23436     insertBefore : function(node, refNode){
23437         if(!refNode){ // like standard Dom, refNode can be null for append
23438             return this.appendChild(node);
23439         }
23440         // nothing to do
23441         if(node == refNode){
23442             return false;
23443         }
23444
23445         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23446             return false;
23447         }
23448         var index = this.childNodes.indexOf(refNode);
23449         var oldParent = node.parentNode;
23450         var refIndex = index;
23451
23452         // when moving internally, indexes will change after remove
23453         if(oldParent == this && this.childNodes.indexOf(node) < index){
23454             refIndex--;
23455         }
23456
23457         // it's a move, make sure we move it cleanly
23458         if(oldParent){
23459             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23460                 return false;
23461             }
23462             oldParent.removeChild(node);
23463         }
23464         if(refIndex == 0){
23465             this.setFirstChild(node);
23466         }
23467         this.childNodes.splice(refIndex, 0, node);
23468         node.parentNode = this;
23469         var ps = this.childNodes[refIndex-1];
23470         if(ps){
23471             node.previousSibling = ps;
23472             ps.nextSibling = node;
23473         }else{
23474             node.previousSibling = null;
23475         }
23476         node.nextSibling = refNode;
23477         refNode.previousSibling = node;
23478         node.setOwnerTree(this.getOwnerTree());
23479         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23480         if(oldParent){
23481             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23482         }
23483         return node;
23484     },
23485
23486     /**
23487      * Returns the child node at the specified index.
23488      * @param {Number} index
23489      * @return {Node}
23490      */
23491     item : function(index){
23492         return this.childNodes[index];
23493     },
23494
23495     /**
23496      * Replaces one child node in this node with another.
23497      * @param {Node} newChild The replacement node
23498      * @param {Node} oldChild The node to replace
23499      * @return {Node} The replaced node
23500      */
23501     replaceChild : function(newChild, oldChild){
23502         this.insertBefore(newChild, oldChild);
23503         this.removeChild(oldChild);
23504         return oldChild;
23505     },
23506
23507     /**
23508      * Returns the index of a child node
23509      * @param {Node} node
23510      * @return {Number} The index of the node or -1 if it was not found
23511      */
23512     indexOf : function(child){
23513         return this.childNodes.indexOf(child);
23514     },
23515
23516     /**
23517      * Returns the tree this node is in.
23518      * @return {Tree}
23519      */
23520     getOwnerTree : function(){
23521         // if it doesn't have one, look for one
23522         if(!this.ownerTree){
23523             var p = this;
23524             while(p){
23525                 if(p.ownerTree){
23526                     this.ownerTree = p.ownerTree;
23527                     break;
23528                 }
23529                 p = p.parentNode;
23530             }
23531         }
23532         return this.ownerTree;
23533     },
23534
23535     /**
23536      * Returns depth of this node (the root node has a depth of 0)
23537      * @return {Number}
23538      */
23539     getDepth : function(){
23540         var depth = 0;
23541         var p = this;
23542         while(p.parentNode){
23543             ++depth;
23544             p = p.parentNode;
23545         }
23546         return depth;
23547     },
23548
23549     // private
23550     setOwnerTree : function(tree){
23551         // if it's move, we need to update everyone
23552         if(tree != this.ownerTree){
23553             if(this.ownerTree){
23554                 this.ownerTree.unregisterNode(this);
23555             }
23556             this.ownerTree = tree;
23557             var cs = this.childNodes;
23558             for(var i = 0, len = cs.length; i < len; i++) {
23559                 cs[i].setOwnerTree(tree);
23560             }
23561             if(tree){
23562                 tree.registerNode(this);
23563             }
23564         }
23565     },
23566
23567     /**
23568      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23569      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23570      * @return {String} The path
23571      */
23572     getPath : function(attr){
23573         attr = attr || "id";
23574         var p = this.parentNode;
23575         var b = [this.attributes[attr]];
23576         while(p){
23577             b.unshift(p.attributes[attr]);
23578             p = p.parentNode;
23579         }
23580         var sep = this.getOwnerTree().pathSeparator;
23581         return sep + b.join(sep);
23582     },
23583
23584     /**
23585      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23586      * function call will be the scope provided or the current node. The arguments to the function
23587      * will be the args provided or the current node. If the function returns false at any point,
23588      * the bubble is stopped.
23589      * @param {Function} fn The function to call
23590      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23591      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23592      */
23593     bubble : function(fn, scope, args){
23594         var p = this;
23595         while(p){
23596             if(fn.call(scope || p, args || p) === false){
23597                 break;
23598             }
23599             p = p.parentNode;
23600         }
23601     },
23602
23603     /**
23604      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23605      * function call will be the scope provided or the current node. The arguments to the function
23606      * will be the args provided or the current node. If the function returns false at any point,
23607      * the cascade is stopped on that branch.
23608      * @param {Function} fn The function to call
23609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23611      */
23612     cascade : function(fn, scope, args){
23613         if(fn.call(scope || this, args || this) !== false){
23614             var cs = this.childNodes;
23615             for(var i = 0, len = cs.length; i < len; i++) {
23616                 cs[i].cascade(fn, scope, args);
23617             }
23618         }
23619     },
23620
23621     /**
23622      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23623      * function call will be the scope provided or the current node. The arguments to the function
23624      * will be the args provided or the current node. If the function returns false at any point,
23625      * the iteration stops.
23626      * @param {Function} fn The function to call
23627      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23628      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23629      */
23630     eachChild : function(fn, scope, args){
23631         var cs = this.childNodes;
23632         for(var i = 0, len = cs.length; i < len; i++) {
23633                 if(fn.call(scope || this, args || cs[i]) === false){
23634                     break;
23635                 }
23636         }
23637     },
23638
23639     /**
23640      * Finds the first child that has the attribute with the specified value.
23641      * @param {String} attribute The attribute name
23642      * @param {Mixed} value The value to search for
23643      * @return {Node} The found child or null if none was found
23644      */
23645     findChild : function(attribute, value){
23646         var cs = this.childNodes;
23647         for(var i = 0, len = cs.length; i < len; i++) {
23648                 if(cs[i].attributes[attribute] == value){
23649                     return cs[i];
23650                 }
23651         }
23652         return null;
23653     },
23654
23655     /**
23656      * Finds the first child by a custom function. The child matches if the function passed
23657      * returns true.
23658      * @param {Function} fn
23659      * @param {Object} scope (optional)
23660      * @return {Node} The found child or null if none was found
23661      */
23662     findChildBy : function(fn, scope){
23663         var cs = this.childNodes;
23664         for(var i = 0, len = cs.length; i < len; i++) {
23665                 if(fn.call(scope||cs[i], cs[i]) === true){
23666                     return cs[i];
23667                 }
23668         }
23669         return null;
23670     },
23671
23672     /**
23673      * Sorts this nodes children using the supplied sort function
23674      * @param {Function} fn
23675      * @param {Object} scope (optional)
23676      */
23677     sort : function(fn, scope){
23678         var cs = this.childNodes;
23679         var len = cs.length;
23680         if(len > 0){
23681             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23682             cs.sort(sortFn);
23683             for(var i = 0; i < len; i++){
23684                 var n = cs[i];
23685                 n.previousSibling = cs[i-1];
23686                 n.nextSibling = cs[i+1];
23687                 if(i == 0){
23688                     this.setFirstChild(n);
23689                 }
23690                 if(i == len-1){
23691                     this.setLastChild(n);
23692                 }
23693             }
23694         }
23695     },
23696
23697     /**
23698      * Returns true if this node is an ancestor (at any point) of the passed node.
23699      * @param {Node} node
23700      * @return {Boolean}
23701      */
23702     contains : function(node){
23703         return node.isAncestor(this);
23704     },
23705
23706     /**
23707      * Returns true if the passed node is an ancestor (at any point) of this node.
23708      * @param {Node} node
23709      * @return {Boolean}
23710      */
23711     isAncestor : function(node){
23712         var p = this.parentNode;
23713         while(p){
23714             if(p == node){
23715                 return true;
23716             }
23717             p = p.parentNode;
23718         }
23719         return false;
23720     },
23721
23722     toString : function(){
23723         return "[Node"+(this.id?" "+this.id:"")+"]";
23724     }
23725 });/*
23726  * Based on:
23727  * Ext JS Library 1.1.1
23728  * Copyright(c) 2006-2007, Ext JS, LLC.
23729  *
23730  * Originally Released Under LGPL - original licence link has changed is not relivant.
23731  *
23732  * Fork - LGPL
23733  * <script type="text/javascript">
23734  */
23735  (function(){ 
23736 /**
23737  * @class Roo.Layer
23738  * @extends Roo.Element
23739  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23740  * automatic maintaining of shadow/shim positions.
23741  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23742  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23743  * you can pass a string with a CSS class name. False turns off the shadow.
23744  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23745  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23746  * @cfg {String} cls CSS class to add to the element
23747  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23748  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23749  * @constructor
23750  * @param {Object} config An object with config options.
23751  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23752  */
23753
23754 Roo.Layer = function(config, existingEl){
23755     config = config || {};
23756     var dh = Roo.DomHelper;
23757     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23758     if(existingEl){
23759         this.dom = Roo.getDom(existingEl);
23760     }
23761     if(!this.dom){
23762         var o = config.dh || {tag: "div", cls: "x-layer"};
23763         this.dom = dh.append(pel, o);
23764     }
23765     if(config.cls){
23766         this.addClass(config.cls);
23767     }
23768     this.constrain = config.constrain !== false;
23769     this.visibilityMode = Roo.Element.VISIBILITY;
23770     if(config.id){
23771         this.id = this.dom.id = config.id;
23772     }else{
23773         this.id = Roo.id(this.dom);
23774     }
23775     this.zindex = config.zindex || this.getZIndex();
23776     this.position("absolute", this.zindex);
23777     if(config.shadow){
23778         this.shadowOffset = config.shadowOffset || 4;
23779         this.shadow = new Roo.Shadow({
23780             offset : this.shadowOffset,
23781             mode : config.shadow
23782         });
23783     }else{
23784         this.shadowOffset = 0;
23785     }
23786     this.useShim = config.shim !== false && Roo.useShims;
23787     this.useDisplay = config.useDisplay;
23788     this.hide();
23789 };
23790
23791 var supr = Roo.Element.prototype;
23792
23793 // shims are shared among layer to keep from having 100 iframes
23794 var shims = [];
23795
23796 Roo.extend(Roo.Layer, Roo.Element, {
23797
23798     getZIndex : function(){
23799         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23800     },
23801
23802     getShim : function(){
23803         if(!this.useShim){
23804             return null;
23805         }
23806         if(this.shim){
23807             return this.shim;
23808         }
23809         var shim = shims.shift();
23810         if(!shim){
23811             shim = this.createShim();
23812             shim.enableDisplayMode('block');
23813             shim.dom.style.display = 'none';
23814             shim.dom.style.visibility = 'visible';
23815         }
23816         var pn = this.dom.parentNode;
23817         if(shim.dom.parentNode != pn){
23818             pn.insertBefore(shim.dom, this.dom);
23819         }
23820         shim.setStyle('z-index', this.getZIndex()-2);
23821         this.shim = shim;
23822         return shim;
23823     },
23824
23825     hideShim : function(){
23826         if(this.shim){
23827             this.shim.setDisplayed(false);
23828             shims.push(this.shim);
23829             delete this.shim;
23830         }
23831     },
23832
23833     disableShadow : function(){
23834         if(this.shadow){
23835             this.shadowDisabled = true;
23836             this.shadow.hide();
23837             this.lastShadowOffset = this.shadowOffset;
23838             this.shadowOffset = 0;
23839         }
23840     },
23841
23842     enableShadow : function(show){
23843         if(this.shadow){
23844             this.shadowDisabled = false;
23845             this.shadowOffset = this.lastShadowOffset;
23846             delete this.lastShadowOffset;
23847             if(show){
23848                 this.sync(true);
23849             }
23850         }
23851     },
23852
23853     // private
23854     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23855     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23856     sync : function(doShow){
23857         var sw = this.shadow;
23858         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23859             var sh = this.getShim();
23860
23861             var w = this.getWidth(),
23862                 h = this.getHeight();
23863
23864             var l = this.getLeft(true),
23865                 t = this.getTop(true);
23866
23867             if(sw && !this.shadowDisabled){
23868                 if(doShow && !sw.isVisible()){
23869                     sw.show(this);
23870                 }else{
23871                     sw.realign(l, t, w, h);
23872                 }
23873                 if(sh){
23874                     if(doShow){
23875                        sh.show();
23876                     }
23877                     // fit the shim behind the shadow, so it is shimmed too
23878                     var a = sw.adjusts, s = sh.dom.style;
23879                     s.left = (Math.min(l, l+a.l))+"px";
23880                     s.top = (Math.min(t, t+a.t))+"px";
23881                     s.width = (w+a.w)+"px";
23882                     s.height = (h+a.h)+"px";
23883                 }
23884             }else if(sh){
23885                 if(doShow){
23886                    sh.show();
23887                 }
23888                 sh.setSize(w, h);
23889                 sh.setLeftTop(l, t);
23890             }
23891             
23892         }
23893     },
23894
23895     // private
23896     destroy : function(){
23897         this.hideShim();
23898         if(this.shadow){
23899             this.shadow.hide();
23900         }
23901         this.removeAllListeners();
23902         var pn = this.dom.parentNode;
23903         if(pn){
23904             pn.removeChild(this.dom);
23905         }
23906         Roo.Element.uncache(this.id);
23907     },
23908
23909     remove : function(){
23910         this.destroy();
23911     },
23912
23913     // private
23914     beginUpdate : function(){
23915         this.updating = true;
23916     },
23917
23918     // private
23919     endUpdate : function(){
23920         this.updating = false;
23921         this.sync(true);
23922     },
23923
23924     // private
23925     hideUnders : function(negOffset){
23926         if(this.shadow){
23927             this.shadow.hide();
23928         }
23929         this.hideShim();
23930     },
23931
23932     // private
23933     constrainXY : function(){
23934         if(this.constrain){
23935             var vw = Roo.lib.Dom.getViewWidth(),
23936                 vh = Roo.lib.Dom.getViewHeight();
23937             var s = Roo.get(document).getScroll();
23938
23939             var xy = this.getXY();
23940             var x = xy[0], y = xy[1];   
23941             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23942             // only move it if it needs it
23943             var moved = false;
23944             // first validate right/bottom
23945             if((x + w) > vw+s.left){
23946                 x = vw - w - this.shadowOffset;
23947                 moved = true;
23948             }
23949             if((y + h) > vh+s.top){
23950                 y = vh - h - this.shadowOffset;
23951                 moved = true;
23952             }
23953             // then make sure top/left isn't negative
23954             if(x < s.left){
23955                 x = s.left;
23956                 moved = true;
23957             }
23958             if(y < s.top){
23959                 y = s.top;
23960                 moved = true;
23961             }
23962             if(moved){
23963                 if(this.avoidY){
23964                     var ay = this.avoidY;
23965                     if(y <= ay && (y+h) >= ay){
23966                         y = ay-h-5;   
23967                     }
23968                 }
23969                 xy = [x, y];
23970                 this.storeXY(xy);
23971                 supr.setXY.call(this, xy);
23972                 this.sync();
23973             }
23974         }
23975     },
23976
23977     isVisible : function(){
23978         return this.visible;    
23979     },
23980
23981     // private
23982     showAction : function(){
23983         this.visible = true; // track visibility to prevent getStyle calls
23984         if(this.useDisplay === true){
23985             this.setDisplayed("");
23986         }else if(this.lastXY){
23987             supr.setXY.call(this, this.lastXY);
23988         }else if(this.lastLT){
23989             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23990         }
23991     },
23992
23993     // private
23994     hideAction : function(){
23995         this.visible = false;
23996         if(this.useDisplay === true){
23997             this.setDisplayed(false);
23998         }else{
23999             this.setLeftTop(-10000,-10000);
24000         }
24001     },
24002
24003     // overridden Element method
24004     setVisible : function(v, a, d, c, e){
24005         if(v){
24006             this.showAction();
24007         }
24008         if(a && v){
24009             var cb = function(){
24010                 this.sync(true);
24011                 if(c){
24012                     c();
24013                 }
24014             }.createDelegate(this);
24015             supr.setVisible.call(this, true, true, d, cb, e);
24016         }else{
24017             if(!v){
24018                 this.hideUnders(true);
24019             }
24020             var cb = c;
24021             if(a){
24022                 cb = function(){
24023                     this.hideAction();
24024                     if(c){
24025                         c();
24026                     }
24027                 }.createDelegate(this);
24028             }
24029             supr.setVisible.call(this, v, a, d, cb, e);
24030             if(v){
24031                 this.sync(true);
24032             }else if(!a){
24033                 this.hideAction();
24034             }
24035         }
24036     },
24037
24038     storeXY : function(xy){
24039         delete this.lastLT;
24040         this.lastXY = xy;
24041     },
24042
24043     storeLeftTop : function(left, top){
24044         delete this.lastXY;
24045         this.lastLT = [left, top];
24046     },
24047
24048     // private
24049     beforeFx : function(){
24050         this.beforeAction();
24051         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24052     },
24053
24054     // private
24055     afterFx : function(){
24056         Roo.Layer.superclass.afterFx.apply(this, arguments);
24057         this.sync(this.isVisible());
24058     },
24059
24060     // private
24061     beforeAction : function(){
24062         if(!this.updating && this.shadow){
24063             this.shadow.hide();
24064         }
24065     },
24066
24067     // overridden Element method
24068     setLeft : function(left){
24069         this.storeLeftTop(left, this.getTop(true));
24070         supr.setLeft.apply(this, arguments);
24071         this.sync();
24072     },
24073
24074     setTop : function(top){
24075         this.storeLeftTop(this.getLeft(true), top);
24076         supr.setTop.apply(this, arguments);
24077         this.sync();
24078     },
24079
24080     setLeftTop : function(left, top){
24081         this.storeLeftTop(left, top);
24082         supr.setLeftTop.apply(this, arguments);
24083         this.sync();
24084     },
24085
24086     setXY : function(xy, a, d, c, e){
24087         this.fixDisplay();
24088         this.beforeAction();
24089         this.storeXY(xy);
24090         var cb = this.createCB(c);
24091         supr.setXY.call(this, xy, a, d, cb, e);
24092         if(!a){
24093             cb();
24094         }
24095     },
24096
24097     // private
24098     createCB : function(c){
24099         var el = this;
24100         return function(){
24101             el.constrainXY();
24102             el.sync(true);
24103             if(c){
24104                 c();
24105             }
24106         };
24107     },
24108
24109     // overridden Element method
24110     setX : function(x, a, d, c, e){
24111         this.setXY([x, this.getY()], a, d, c, e);
24112     },
24113
24114     // overridden Element method
24115     setY : function(y, a, d, c, e){
24116         this.setXY([this.getX(), y], a, d, c, e);
24117     },
24118
24119     // overridden Element method
24120     setSize : function(w, h, a, d, c, e){
24121         this.beforeAction();
24122         var cb = this.createCB(c);
24123         supr.setSize.call(this, w, h, a, d, cb, e);
24124         if(!a){
24125             cb();
24126         }
24127     },
24128
24129     // overridden Element method
24130     setWidth : function(w, a, d, c, e){
24131         this.beforeAction();
24132         var cb = this.createCB(c);
24133         supr.setWidth.call(this, w, a, d, cb, e);
24134         if(!a){
24135             cb();
24136         }
24137     },
24138
24139     // overridden Element method
24140     setHeight : function(h, a, d, c, e){
24141         this.beforeAction();
24142         var cb = this.createCB(c);
24143         supr.setHeight.call(this, h, a, d, cb, e);
24144         if(!a){
24145             cb();
24146         }
24147     },
24148
24149     // overridden Element method
24150     setBounds : function(x, y, w, h, a, d, c, e){
24151         this.beforeAction();
24152         var cb = this.createCB(c);
24153         if(!a){
24154             this.storeXY([x, y]);
24155             supr.setXY.call(this, [x, y]);
24156             supr.setSize.call(this, w, h, a, d, cb, e);
24157             cb();
24158         }else{
24159             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24160         }
24161         return this;
24162     },
24163     
24164     /**
24165      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24166      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24167      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24168      * @param {Number} zindex The new z-index to set
24169      * @return {this} The Layer
24170      */
24171     setZIndex : function(zindex){
24172         this.zindex = zindex;
24173         this.setStyle("z-index", zindex + 2);
24174         if(this.shadow){
24175             this.shadow.setZIndex(zindex + 1);
24176         }
24177         if(this.shim){
24178             this.shim.setStyle("z-index", zindex);
24179         }
24180     }
24181 });
24182 })();/*
24183  * Based on:
24184  * Ext JS Library 1.1.1
24185  * Copyright(c) 2006-2007, Ext JS, LLC.
24186  *
24187  * Originally Released Under LGPL - original licence link has changed is not relivant.
24188  *
24189  * Fork - LGPL
24190  * <script type="text/javascript">
24191  */
24192
24193
24194 /**
24195  * @class Roo.Shadow
24196  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24197  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24198  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24199  * @constructor
24200  * Create a new Shadow
24201  * @param {Object} config The config object
24202  */
24203 Roo.Shadow = function(config){
24204     Roo.apply(this, config);
24205     if(typeof this.mode != "string"){
24206         this.mode = this.defaultMode;
24207     }
24208     var o = this.offset, a = {h: 0};
24209     var rad = Math.floor(this.offset/2);
24210     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24211         case "drop":
24212             a.w = 0;
24213             a.l = a.t = o;
24214             a.t -= 1;
24215             if(Roo.isIE){
24216                 a.l -= this.offset + rad;
24217                 a.t -= this.offset + rad;
24218                 a.w -= rad;
24219                 a.h -= rad;
24220                 a.t += 1;
24221             }
24222         break;
24223         case "sides":
24224             a.w = (o*2);
24225             a.l = -o;
24226             a.t = o-1;
24227             if(Roo.isIE){
24228                 a.l -= (this.offset - rad);
24229                 a.t -= this.offset + rad;
24230                 a.l += 1;
24231                 a.w -= (this.offset - rad)*2;
24232                 a.w -= rad + 1;
24233                 a.h -= 1;
24234             }
24235         break;
24236         case "frame":
24237             a.w = a.h = (o*2);
24238             a.l = a.t = -o;
24239             a.t += 1;
24240             a.h -= 2;
24241             if(Roo.isIE){
24242                 a.l -= (this.offset - rad);
24243                 a.t -= (this.offset - rad);
24244                 a.l += 1;
24245                 a.w -= (this.offset + rad + 1);
24246                 a.h -= (this.offset + rad);
24247                 a.h += 1;
24248             }
24249         break;
24250     };
24251
24252     this.adjusts = a;
24253 };
24254
24255 Roo.Shadow.prototype = {
24256     /**
24257      * @cfg {String} mode
24258      * The shadow display mode.  Supports the following options:<br />
24259      * sides: Shadow displays on both sides and bottom only<br />
24260      * frame: Shadow displays equally on all four sides<br />
24261      * drop: Traditional bottom-right drop shadow (default)
24262      */
24263     /**
24264      * @cfg {String} offset
24265      * The number of pixels to offset the shadow from the element (defaults to 4)
24266      */
24267     offset: 4,
24268
24269     // private
24270     defaultMode: "drop",
24271
24272     /**
24273      * Displays the shadow under the target element
24274      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24275      */
24276     show : function(target){
24277         target = Roo.get(target);
24278         if(!this.el){
24279             this.el = Roo.Shadow.Pool.pull();
24280             if(this.el.dom.nextSibling != target.dom){
24281                 this.el.insertBefore(target);
24282             }
24283         }
24284         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24285         if(Roo.isIE){
24286             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24287         }
24288         this.realign(
24289             target.getLeft(true),
24290             target.getTop(true),
24291             target.getWidth(),
24292             target.getHeight()
24293         );
24294         this.el.dom.style.display = "block";
24295     },
24296
24297     /**
24298      * Returns true if the shadow is visible, else false
24299      */
24300     isVisible : function(){
24301         return this.el ? true : false;  
24302     },
24303
24304     /**
24305      * Direct alignment when values are already available. Show must be called at least once before
24306      * calling this method to ensure it is initialized.
24307      * @param {Number} left The target element left position
24308      * @param {Number} top The target element top position
24309      * @param {Number} width The target element width
24310      * @param {Number} height The target element height
24311      */
24312     realign : function(l, t, w, h){
24313         if(!this.el){
24314             return;
24315         }
24316         var a = this.adjusts, d = this.el.dom, s = d.style;
24317         var iea = 0;
24318         s.left = (l+a.l)+"px";
24319         s.top = (t+a.t)+"px";
24320         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24321  
24322         if(s.width != sws || s.height != shs){
24323             s.width = sws;
24324             s.height = shs;
24325             if(!Roo.isIE){
24326                 var cn = d.childNodes;
24327                 var sww = Math.max(0, (sw-12))+"px";
24328                 cn[0].childNodes[1].style.width = sww;
24329                 cn[1].childNodes[1].style.width = sww;
24330                 cn[2].childNodes[1].style.width = sww;
24331                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24332             }
24333         }
24334     },
24335
24336     /**
24337      * Hides this shadow
24338      */
24339     hide : function(){
24340         if(this.el){
24341             this.el.dom.style.display = "none";
24342             Roo.Shadow.Pool.push(this.el);
24343             delete this.el;
24344         }
24345     },
24346
24347     /**
24348      * Adjust the z-index of this shadow
24349      * @param {Number} zindex The new z-index
24350      */
24351     setZIndex : function(z){
24352         this.zIndex = z;
24353         if(this.el){
24354             this.el.setStyle("z-index", z);
24355         }
24356     }
24357 };
24358
24359 // Private utility class that manages the internal Shadow cache
24360 Roo.Shadow.Pool = function(){
24361     var p = [];
24362     var markup = Roo.isIE ?
24363                  '<div class="x-ie-shadow"></div>' :
24364                  '<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>';
24365     return {
24366         pull : function(){
24367             var sh = p.shift();
24368             if(!sh){
24369                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24370                 sh.autoBoxAdjust = false;
24371             }
24372             return sh;
24373         },
24374
24375         push : function(sh){
24376             p.push(sh);
24377         }
24378     };
24379 }();/*
24380  * Based on:
24381  * Ext JS Library 1.1.1
24382  * Copyright(c) 2006-2007, Ext JS, LLC.
24383  *
24384  * Originally Released Under LGPL - original licence link has changed is not relivant.
24385  *
24386  * Fork - LGPL
24387  * <script type="text/javascript">
24388  */
24389
24390
24391 /**
24392  * @class Roo.SplitBar
24393  * @extends Roo.util.Observable
24394  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24395  * <br><br>
24396  * Usage:
24397  * <pre><code>
24398 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24399                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24400 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24401 split.minSize = 100;
24402 split.maxSize = 600;
24403 split.animate = true;
24404 split.on('moved', splitterMoved);
24405 </code></pre>
24406  * @constructor
24407  * Create a new SplitBar
24408  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24409  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24410  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24411  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24412                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24413                         position of the SplitBar).
24414  */
24415 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24416     
24417     /** @private */
24418     this.el = Roo.get(dragElement, true);
24419     this.el.dom.unselectable = "on";
24420     /** @private */
24421     this.resizingEl = Roo.get(resizingElement, true);
24422
24423     /**
24424      * @private
24425      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24426      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24427      * @type Number
24428      */
24429     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24430     
24431     /**
24432      * The minimum size of the resizing element. (Defaults to 0)
24433      * @type Number
24434      */
24435     this.minSize = 0;
24436     
24437     /**
24438      * The maximum size of the resizing element. (Defaults to 2000)
24439      * @type Number
24440      */
24441     this.maxSize = 2000;
24442     
24443     /**
24444      * Whether to animate the transition to the new size
24445      * @type Boolean
24446      */
24447     this.animate = false;
24448     
24449     /**
24450      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24451      * @type Boolean
24452      */
24453     this.useShim = false;
24454     
24455     /** @private */
24456     this.shim = null;
24457     
24458     if(!existingProxy){
24459         /** @private */
24460         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24461     }else{
24462         this.proxy = Roo.get(existingProxy).dom;
24463     }
24464     /** @private */
24465     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24466     
24467     /** @private */
24468     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24469     
24470     /** @private */
24471     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24472     
24473     /** @private */
24474     this.dragSpecs = {};
24475     
24476     /**
24477      * @private The adapter to use to positon and resize elements
24478      */
24479     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24480     this.adapter.init(this);
24481     
24482     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24483         /** @private */
24484         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24485         this.el.addClass("x-splitbar-h");
24486     }else{
24487         /** @private */
24488         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24489         this.el.addClass("x-splitbar-v");
24490     }
24491     
24492     this.addEvents({
24493         /**
24494          * @event resize
24495          * Fires when the splitter is moved (alias for {@link #event-moved})
24496          * @param {Roo.SplitBar} this
24497          * @param {Number} newSize the new width or height
24498          */
24499         "resize" : true,
24500         /**
24501          * @event moved
24502          * Fires when the splitter is moved
24503          * @param {Roo.SplitBar} this
24504          * @param {Number} newSize the new width or height
24505          */
24506         "moved" : true,
24507         /**
24508          * @event beforeresize
24509          * Fires before the splitter is dragged
24510          * @param {Roo.SplitBar} this
24511          */
24512         "beforeresize" : true,
24513
24514         "beforeapply" : true
24515     });
24516
24517     Roo.util.Observable.call(this);
24518 };
24519
24520 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24521     onStartProxyDrag : function(x, y){
24522         this.fireEvent("beforeresize", this);
24523         if(!this.overlay){
24524             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24525             o.unselectable();
24526             o.enableDisplayMode("block");
24527             // all splitbars share the same overlay
24528             Roo.SplitBar.prototype.overlay = o;
24529         }
24530         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24531         this.overlay.show();
24532         Roo.get(this.proxy).setDisplayed("block");
24533         var size = this.adapter.getElementSize(this);
24534         this.activeMinSize = this.getMinimumSize();;
24535         this.activeMaxSize = this.getMaximumSize();;
24536         var c1 = size - this.activeMinSize;
24537         var c2 = Math.max(this.activeMaxSize - size, 0);
24538         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24539             this.dd.resetConstraints();
24540             this.dd.setXConstraint(
24541                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24542                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24543             );
24544             this.dd.setYConstraint(0, 0);
24545         }else{
24546             this.dd.resetConstraints();
24547             this.dd.setXConstraint(0, 0);
24548             this.dd.setYConstraint(
24549                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24550                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24551             );
24552          }
24553         this.dragSpecs.startSize = size;
24554         this.dragSpecs.startPoint = [x, y];
24555         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24556     },
24557     
24558     /** 
24559      * @private Called after the drag operation by the DDProxy
24560      */
24561     onEndProxyDrag : function(e){
24562         Roo.get(this.proxy).setDisplayed(false);
24563         var endPoint = Roo.lib.Event.getXY(e);
24564         if(this.overlay){
24565             this.overlay.hide();
24566         }
24567         var newSize;
24568         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24569             newSize = this.dragSpecs.startSize + 
24570                 (this.placement == Roo.SplitBar.LEFT ?
24571                     endPoint[0] - this.dragSpecs.startPoint[0] :
24572                     this.dragSpecs.startPoint[0] - endPoint[0]
24573                 );
24574         }else{
24575             newSize = this.dragSpecs.startSize + 
24576                 (this.placement == Roo.SplitBar.TOP ?
24577                     endPoint[1] - this.dragSpecs.startPoint[1] :
24578                     this.dragSpecs.startPoint[1] - endPoint[1]
24579                 );
24580         }
24581         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24582         if(newSize != this.dragSpecs.startSize){
24583             if(this.fireEvent('beforeapply', this, newSize) !== false){
24584                 this.adapter.setElementSize(this, newSize);
24585                 this.fireEvent("moved", this, newSize);
24586                 this.fireEvent("resize", this, newSize);
24587             }
24588         }
24589     },
24590     
24591     /**
24592      * Get the adapter this SplitBar uses
24593      * @return The adapter object
24594      */
24595     getAdapter : function(){
24596         return this.adapter;
24597     },
24598     
24599     /**
24600      * Set the adapter this SplitBar uses
24601      * @param {Object} adapter A SplitBar adapter object
24602      */
24603     setAdapter : function(adapter){
24604         this.adapter = adapter;
24605         this.adapter.init(this);
24606     },
24607     
24608     /**
24609      * Gets the minimum size for the resizing element
24610      * @return {Number} The minimum size
24611      */
24612     getMinimumSize : function(){
24613         return this.minSize;
24614     },
24615     
24616     /**
24617      * Sets the minimum size for the resizing element
24618      * @param {Number} minSize The minimum size
24619      */
24620     setMinimumSize : function(minSize){
24621         this.minSize = minSize;
24622     },
24623     
24624     /**
24625      * Gets the maximum size for the resizing element
24626      * @return {Number} The maximum size
24627      */
24628     getMaximumSize : function(){
24629         return this.maxSize;
24630     },
24631     
24632     /**
24633      * Sets the maximum size for the resizing element
24634      * @param {Number} maxSize The maximum size
24635      */
24636     setMaximumSize : function(maxSize){
24637         this.maxSize = maxSize;
24638     },
24639     
24640     /**
24641      * Sets the initialize size for the resizing element
24642      * @param {Number} size The initial size
24643      */
24644     setCurrentSize : function(size){
24645         var oldAnimate = this.animate;
24646         this.animate = false;
24647         this.adapter.setElementSize(this, size);
24648         this.animate = oldAnimate;
24649     },
24650     
24651     /**
24652      * Destroy this splitbar. 
24653      * @param {Boolean} removeEl True to remove the element
24654      */
24655     destroy : function(removeEl){
24656         if(this.shim){
24657             this.shim.remove();
24658         }
24659         this.dd.unreg();
24660         this.proxy.parentNode.removeChild(this.proxy);
24661         if(removeEl){
24662             this.el.remove();
24663         }
24664     }
24665 });
24666
24667 /**
24668  * @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.
24669  */
24670 Roo.SplitBar.createProxy = function(dir){
24671     var proxy = new Roo.Element(document.createElement("div"));
24672     proxy.unselectable();
24673     var cls = 'x-splitbar-proxy';
24674     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24675     document.body.appendChild(proxy.dom);
24676     return proxy.dom;
24677 };
24678
24679 /** 
24680  * @class Roo.SplitBar.BasicLayoutAdapter
24681  * Default Adapter. It assumes the splitter and resizing element are not positioned
24682  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24683  */
24684 Roo.SplitBar.BasicLayoutAdapter = function(){
24685 };
24686
24687 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24688     // do nothing for now
24689     init : function(s){
24690     
24691     },
24692     /**
24693      * Called before drag operations to get the current size of the resizing element. 
24694      * @param {Roo.SplitBar} s The SplitBar using this adapter
24695      */
24696      getElementSize : function(s){
24697         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24698             return s.resizingEl.getWidth();
24699         }else{
24700             return s.resizingEl.getHeight();
24701         }
24702     },
24703     
24704     /**
24705      * Called after drag operations to set the size of the resizing element.
24706      * @param {Roo.SplitBar} s The SplitBar using this adapter
24707      * @param {Number} newSize The new size to set
24708      * @param {Function} onComplete A function to be invoked when resizing is complete
24709      */
24710     setElementSize : function(s, newSize, onComplete){
24711         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24712             if(!s.animate){
24713                 s.resizingEl.setWidth(newSize);
24714                 if(onComplete){
24715                     onComplete(s, newSize);
24716                 }
24717             }else{
24718                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24719             }
24720         }else{
24721             
24722             if(!s.animate){
24723                 s.resizingEl.setHeight(newSize);
24724                 if(onComplete){
24725                     onComplete(s, newSize);
24726                 }
24727             }else{
24728                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24729             }
24730         }
24731     }
24732 };
24733
24734 /** 
24735  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24736  * @extends Roo.SplitBar.BasicLayoutAdapter
24737  * Adapter that  moves the splitter element to align with the resized sizing element. 
24738  * Used with an absolute positioned SplitBar.
24739  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24740  * document.body, make sure you assign an id to the body element.
24741  */
24742 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24743     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24744     this.container = Roo.get(container);
24745 };
24746
24747 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24748     init : function(s){
24749         this.basic.init(s);
24750     },
24751     
24752     getElementSize : function(s){
24753         return this.basic.getElementSize(s);
24754     },
24755     
24756     setElementSize : function(s, newSize, onComplete){
24757         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24758     },
24759     
24760     moveSplitter : function(s){
24761         var yes = Roo.SplitBar;
24762         switch(s.placement){
24763             case yes.LEFT:
24764                 s.el.setX(s.resizingEl.getRight());
24765                 break;
24766             case yes.RIGHT:
24767                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24768                 break;
24769             case yes.TOP:
24770                 s.el.setY(s.resizingEl.getBottom());
24771                 break;
24772             case yes.BOTTOM:
24773                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24774                 break;
24775         }
24776     }
24777 };
24778
24779 /**
24780  * Orientation constant - Create a vertical SplitBar
24781  * @static
24782  * @type Number
24783  */
24784 Roo.SplitBar.VERTICAL = 1;
24785
24786 /**
24787  * Orientation constant - Create a horizontal SplitBar
24788  * @static
24789  * @type Number
24790  */
24791 Roo.SplitBar.HORIZONTAL = 2;
24792
24793 /**
24794  * Placement constant - The resizing element is to the left of the splitter element
24795  * @static
24796  * @type Number
24797  */
24798 Roo.SplitBar.LEFT = 1;
24799
24800 /**
24801  * Placement constant - The resizing element is to the right of the splitter element
24802  * @static
24803  * @type Number
24804  */
24805 Roo.SplitBar.RIGHT = 2;
24806
24807 /**
24808  * Placement constant - The resizing element is positioned above the splitter element
24809  * @static
24810  * @type Number
24811  */
24812 Roo.SplitBar.TOP = 3;
24813
24814 /**
24815  * Placement constant - The resizing element is positioned under splitter element
24816  * @static
24817  * @type Number
24818  */
24819 Roo.SplitBar.BOTTOM = 4;
24820 /*
24821  * Based on:
24822  * Ext JS Library 1.1.1
24823  * Copyright(c) 2006-2007, Ext JS, LLC.
24824  *
24825  * Originally Released Under LGPL - original licence link has changed is not relivant.
24826  *
24827  * Fork - LGPL
24828  * <script type="text/javascript">
24829  */
24830
24831 /**
24832  * @class Roo.View
24833  * @extends Roo.util.Observable
24834  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24835  * This class also supports single and multi selection modes. <br>
24836  * Create a data model bound view:
24837  <pre><code>
24838  var store = new Roo.data.Store(...);
24839
24840  var view = new Roo.View({
24841     el : "my-element",
24842     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24843  
24844     singleSelect: true,
24845     selectedClass: "ydataview-selected",
24846     store: store
24847  });
24848
24849  // listen for node click?
24850  view.on("click", function(vw, index, node, e){
24851  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24852  });
24853
24854  // load XML data
24855  dataModel.load("foobar.xml");
24856  </code></pre>
24857  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24858  * <br><br>
24859  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24860  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24861  * 
24862  * Note: old style constructor is still suported (container, template, config)
24863  * 
24864  * @constructor
24865  * Create a new View
24866  * @param {Object} config The config object
24867  * 
24868  */
24869 Roo.View = function(config, depreciated_tpl, depreciated_config){
24870     
24871     this.parent = false;
24872     
24873     if (typeof(depreciated_tpl) == 'undefined') {
24874         // new way.. - universal constructor.
24875         Roo.apply(this, config);
24876         this.el  = Roo.get(this.el);
24877     } else {
24878         // old format..
24879         this.el  = Roo.get(config);
24880         this.tpl = depreciated_tpl;
24881         Roo.apply(this, depreciated_config);
24882     }
24883     this.wrapEl  = this.el.wrap().wrap();
24884     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24885     
24886     
24887     if(typeof(this.tpl) == "string"){
24888         this.tpl = new Roo.Template(this.tpl);
24889     } else {
24890         // support xtype ctors..
24891         this.tpl = new Roo.factory(this.tpl, Roo);
24892     }
24893     
24894     
24895     this.tpl.compile();
24896     
24897     /** @private */
24898     this.addEvents({
24899         /**
24900          * @event beforeclick
24901          * Fires before a click is processed. Returns false to cancel the default action.
24902          * @param {Roo.View} this
24903          * @param {Number} index The index of the target node
24904          * @param {HTMLElement} node The target node
24905          * @param {Roo.EventObject} e The raw event object
24906          */
24907             "beforeclick" : true,
24908         /**
24909          * @event click
24910          * Fires when a template node is clicked.
24911          * @param {Roo.View} this
24912          * @param {Number} index The index of the target node
24913          * @param {HTMLElement} node The target node
24914          * @param {Roo.EventObject} e The raw event object
24915          */
24916             "click" : true,
24917         /**
24918          * @event dblclick
24919          * Fires when a template node is double clicked.
24920          * @param {Roo.View} this
24921          * @param {Number} index The index of the target node
24922          * @param {HTMLElement} node The target node
24923          * @param {Roo.EventObject} e The raw event object
24924          */
24925             "dblclick" : true,
24926         /**
24927          * @event contextmenu
24928          * Fires when a template node is right clicked.
24929          * @param {Roo.View} this
24930          * @param {Number} index The index of the target node
24931          * @param {HTMLElement} node The target node
24932          * @param {Roo.EventObject} e The raw event object
24933          */
24934             "contextmenu" : true,
24935         /**
24936          * @event selectionchange
24937          * Fires when the selected nodes change.
24938          * @param {Roo.View} this
24939          * @param {Array} selections Array of the selected nodes
24940          */
24941             "selectionchange" : true,
24942     
24943         /**
24944          * @event beforeselect
24945          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24946          * @param {Roo.View} this
24947          * @param {HTMLElement} node The node to be selected
24948          * @param {Array} selections Array of currently selected nodes
24949          */
24950             "beforeselect" : true,
24951         /**
24952          * @event preparedata
24953          * Fires on every row to render, to allow you to change the data.
24954          * @param {Roo.View} this
24955          * @param {Object} data to be rendered (change this)
24956          */
24957           "preparedata" : true
24958           
24959           
24960         });
24961
24962
24963
24964     this.el.on({
24965         "click": this.onClick,
24966         "dblclick": this.onDblClick,
24967         "contextmenu": this.onContextMenu,
24968         scope:this
24969     });
24970
24971     this.selections = [];
24972     this.nodes = [];
24973     this.cmp = new Roo.CompositeElementLite([]);
24974     if(this.store){
24975         this.store = Roo.factory(this.store, Roo.data);
24976         this.setStore(this.store, true);
24977     }
24978     
24979     if ( this.footer && this.footer.xtype) {
24980            
24981          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24982         
24983         this.footer.dataSource = this.store
24984         this.footer.container = fctr;
24985         this.footer = Roo.factory(this.footer, Roo);
24986         fctr.insertFirst(this.el);
24987         
24988         // this is a bit insane - as the paging toolbar seems to detach the el..
24989 //        dom.parentNode.parentNode.parentNode
24990          // they get detached?
24991     }
24992     
24993     
24994     Roo.View.superclass.constructor.call(this);
24995     
24996     
24997 };
24998
24999 Roo.extend(Roo.View, Roo.util.Observable, {
25000     
25001      /**
25002      * @cfg {Roo.data.Store} store Data store to load data from.
25003      */
25004     store : false,
25005     
25006     /**
25007      * @cfg {String|Roo.Element} el The container element.
25008      */
25009     el : '',
25010     
25011     /**
25012      * @cfg {String|Roo.Template} tpl The template used by this View 
25013      */
25014     tpl : false,
25015     /**
25016      * @cfg {String} dataName the named area of the template to use as the data area
25017      *                          Works with domtemplates roo-name="name"
25018      */
25019     dataName: false,
25020     /**
25021      * @cfg {String} selectedClass The css class to add to selected nodes
25022      */
25023     selectedClass : "x-view-selected",
25024      /**
25025      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25026      */
25027     emptyText : "",
25028     
25029     /**
25030      * @cfg {String} text to display on mask (default Loading)
25031      */
25032     mask : false,
25033     /**
25034      * @cfg {Boolean} multiSelect Allow multiple selection
25035      */
25036     multiSelect : false,
25037     /**
25038      * @cfg {Boolean} singleSelect Allow single selection
25039      */
25040     singleSelect:  false,
25041     
25042     /**
25043      * @cfg {Boolean} toggleSelect - selecting 
25044      */
25045     toggleSelect : false,
25046     
25047     /**
25048      * @cfg {Boolean} tickable - selecting 
25049      */
25050     tickable : false,
25051     
25052     /**
25053      * Returns the element this view is bound to.
25054      * @return {Roo.Element}
25055      */
25056     getEl : function(){
25057         return this.wrapEl;
25058     },
25059     
25060     
25061
25062     /**
25063      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25064      */
25065     refresh : function(){
25066         //Roo.log('refresh');
25067         var t = this.tpl;
25068         
25069         // if we are using something like 'domtemplate', then
25070         // the what gets used is:
25071         // t.applySubtemplate(NAME, data, wrapping data..)
25072         // the outer template then get' applied with
25073         //     the store 'extra data'
25074         // and the body get's added to the
25075         //      roo-name="data" node?
25076         //      <span class='roo-tpl-{name}'></span> ?????
25077         
25078         
25079         
25080         this.clearSelections();
25081         this.el.update("");
25082         var html = [];
25083         var records = this.store.getRange();
25084         if(records.length < 1) {
25085             
25086             // is this valid??  = should it render a template??
25087             
25088             this.el.update(this.emptyText);
25089             return;
25090         }
25091         var el = this.el;
25092         if (this.dataName) {
25093             this.el.update(t.apply(this.store.meta)); //????
25094             el = this.el.child('.roo-tpl-' + this.dataName);
25095         }
25096         
25097         for(var i = 0, len = records.length; i < len; i++){
25098             var data = this.prepareData(records[i].data, i, records[i]);
25099             this.fireEvent("preparedata", this, data, i, records[i]);
25100             
25101             var d = Roo.apply({}, data);
25102             
25103             if(this.tickable){
25104                 Roo.apply(d, {'roo-id' : Roo.id()});
25105                 
25106                 var _this = this;
25107             
25108                 Roo.each(this.parent.item, function(item){
25109                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25110                         return;
25111                     }
25112                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25113                 });
25114             }
25115             
25116             html[html.length] = Roo.util.Format.trim(
25117                 this.dataName ?
25118                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25119                     t.apply(d)
25120             );
25121         }
25122         
25123         
25124         
25125         el.update(html.join(""));
25126         this.nodes = el.dom.childNodes;
25127         this.updateIndexes(0);
25128     },
25129     
25130
25131     /**
25132      * Function to override to reformat the data that is sent to
25133      * the template for each node.
25134      * DEPRICATED - use the preparedata event handler.
25135      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25136      * a JSON object for an UpdateManager bound view).
25137      */
25138     prepareData : function(data, index, record)
25139     {
25140         this.fireEvent("preparedata", this, data, index, record);
25141         return data;
25142     },
25143
25144     onUpdate : function(ds, record){
25145         // Roo.log('on update');   
25146         this.clearSelections();
25147         var index = this.store.indexOf(record);
25148         var n = this.nodes[index];
25149         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25150         n.parentNode.removeChild(n);
25151         this.updateIndexes(index, index);
25152     },
25153
25154     
25155     
25156 // --------- FIXME     
25157     onAdd : function(ds, records, index)
25158     {
25159         //Roo.log(['on Add', ds, records, index] );        
25160         this.clearSelections();
25161         if(this.nodes.length == 0){
25162             this.refresh();
25163             return;
25164         }
25165         var n = this.nodes[index];
25166         for(var i = 0, len = records.length; i < len; i++){
25167             var d = this.prepareData(records[i].data, i, records[i]);
25168             if(n){
25169                 this.tpl.insertBefore(n, d);
25170             }else{
25171                 
25172                 this.tpl.append(this.el, d);
25173             }
25174         }
25175         this.updateIndexes(index);
25176     },
25177
25178     onRemove : function(ds, record, index){
25179        // Roo.log('onRemove');
25180         this.clearSelections();
25181         var el = this.dataName  ?
25182             this.el.child('.roo-tpl-' + this.dataName) :
25183             this.el; 
25184         
25185         el.dom.removeChild(this.nodes[index]);
25186         this.updateIndexes(index);
25187     },
25188
25189     /**
25190      * Refresh an individual node.
25191      * @param {Number} index
25192      */
25193     refreshNode : function(index){
25194         this.onUpdate(this.store, this.store.getAt(index));
25195     },
25196
25197     updateIndexes : function(startIndex, endIndex){
25198         var ns = this.nodes;
25199         startIndex = startIndex || 0;
25200         endIndex = endIndex || ns.length - 1;
25201         for(var i = startIndex; i <= endIndex; i++){
25202             ns[i].nodeIndex = i;
25203         }
25204     },
25205
25206     /**
25207      * Changes the data store this view uses and refresh the view.
25208      * @param {Store} store
25209      */
25210     setStore : function(store, initial){
25211         if(!initial && this.store){
25212             this.store.un("datachanged", this.refresh);
25213             this.store.un("add", this.onAdd);
25214             this.store.un("remove", this.onRemove);
25215             this.store.un("update", this.onUpdate);
25216             this.store.un("clear", this.refresh);
25217             this.store.un("beforeload", this.onBeforeLoad);
25218             this.store.un("load", this.onLoad);
25219             this.store.un("loadexception", this.onLoad);
25220         }
25221         if(store){
25222           
25223             store.on("datachanged", this.refresh, this);
25224             store.on("add", this.onAdd, this);
25225             store.on("remove", this.onRemove, this);
25226             store.on("update", this.onUpdate, this);
25227             store.on("clear", this.refresh, this);
25228             store.on("beforeload", this.onBeforeLoad, this);
25229             store.on("load", this.onLoad, this);
25230             store.on("loadexception", this.onLoad, this);
25231         }
25232         
25233         if(store){
25234             this.refresh();
25235         }
25236     },
25237     /**
25238      * onbeforeLoad - masks the loading area.
25239      *
25240      */
25241     onBeforeLoad : function(store,opts)
25242     {
25243          //Roo.log('onBeforeLoad');   
25244         if (!opts.add) {
25245             this.el.update("");
25246         }
25247         this.el.mask(this.mask ? this.mask : "Loading" ); 
25248     },
25249     onLoad : function ()
25250     {
25251         this.el.unmask();
25252     },
25253     
25254
25255     /**
25256      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25257      * @param {HTMLElement} node
25258      * @return {HTMLElement} The template node
25259      */
25260     findItemFromChild : function(node){
25261         var el = this.dataName  ?
25262             this.el.child('.roo-tpl-' + this.dataName,true) :
25263             this.el.dom; 
25264         
25265         if(!node || node.parentNode == el){
25266                     return node;
25267             }
25268             var p = node.parentNode;
25269             while(p && p != el){
25270             if(p.parentNode == el){
25271                 return p;
25272             }
25273             p = p.parentNode;
25274         }
25275             return null;
25276     },
25277
25278     /** @ignore */
25279     onClick : function(e){
25280         var item = this.findItemFromChild(e.getTarget());
25281         if(item){
25282             var index = this.indexOf(item);
25283             if(this.onItemClick(item, index, e) !== false){
25284                 this.fireEvent("click", this, index, item, e);
25285             }
25286         }else{
25287             this.clearSelections();
25288         }
25289     },
25290
25291     /** @ignore */
25292     onContextMenu : function(e){
25293         var item = this.findItemFromChild(e.getTarget());
25294         if(item){
25295             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25296         }
25297     },
25298
25299     /** @ignore */
25300     onDblClick : function(e){
25301         var item = this.findItemFromChild(e.getTarget());
25302         if(item){
25303             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25304         }
25305     },
25306
25307     onItemClick : function(item, index, e)
25308     {
25309         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25310             return false;
25311         }
25312         if (this.toggleSelect) {
25313             var m = this.isSelected(item) ? 'unselect' : 'select';
25314             //Roo.log(m);
25315             var _t = this;
25316             _t[m](item, true, false);
25317             return true;
25318         }
25319         if(this.multiSelect || this.singleSelect){
25320             if(this.multiSelect && e.shiftKey && this.lastSelection){
25321                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25322             }else{
25323                 this.select(item, this.multiSelect && e.ctrlKey);
25324                 this.lastSelection = item;
25325             }
25326             
25327             if(!this.tickable){
25328                 e.preventDefault();
25329             }
25330             
25331         }
25332         return true;
25333     },
25334
25335     /**
25336      * Get the number of selected nodes.
25337      * @return {Number}
25338      */
25339     getSelectionCount : function(){
25340         return this.selections.length;
25341     },
25342
25343     /**
25344      * Get the currently selected nodes.
25345      * @return {Array} An array of HTMLElements
25346      */
25347     getSelectedNodes : function(){
25348         return this.selections;
25349     },
25350
25351     /**
25352      * Get the indexes of the selected nodes.
25353      * @return {Array}
25354      */
25355     getSelectedIndexes : function(){
25356         var indexes = [], s = this.selections;
25357         for(var i = 0, len = s.length; i < len; i++){
25358             indexes.push(s[i].nodeIndex);
25359         }
25360         return indexes;
25361     },
25362
25363     /**
25364      * Clear all selections
25365      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25366      */
25367     clearSelections : function(suppressEvent){
25368         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25369             this.cmp.elements = this.selections;
25370             this.cmp.removeClass(this.selectedClass);
25371             this.selections = [];
25372             if(!suppressEvent){
25373                 this.fireEvent("selectionchange", this, this.selections);
25374             }
25375         }
25376     },
25377
25378     /**
25379      * Returns true if the passed node is selected
25380      * @param {HTMLElement/Number} node The node or node index
25381      * @return {Boolean}
25382      */
25383     isSelected : function(node){
25384         var s = this.selections;
25385         if(s.length < 1){
25386             return false;
25387         }
25388         node = this.getNode(node);
25389         return s.indexOf(node) !== -1;
25390     },
25391
25392     /**
25393      * Selects nodes.
25394      * @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
25395      * @param {Boolean} keepExisting (optional) true to keep existing selections
25396      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25397      */
25398     select : function(nodeInfo, keepExisting, suppressEvent){
25399         if(nodeInfo instanceof Array){
25400             if(!keepExisting){
25401                 this.clearSelections(true);
25402             }
25403             for(var i = 0, len = nodeInfo.length; i < len; i++){
25404                 this.select(nodeInfo[i], true, true);
25405             }
25406             return;
25407         } 
25408         var node = this.getNode(nodeInfo);
25409         if(!node || this.isSelected(node)){
25410             return; // already selected.
25411         }
25412         if(!keepExisting){
25413             this.clearSelections(true);
25414         }
25415         
25416         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25417             Roo.fly(node).addClass(this.selectedClass);
25418             this.selections.push(node);
25419             if(!suppressEvent){
25420                 this.fireEvent("selectionchange", this, this.selections);
25421             }
25422         }
25423         
25424         
25425     },
25426       /**
25427      * Unselects nodes.
25428      * @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
25429      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25430      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25431      */
25432     unselect : function(nodeInfo, keepExisting, suppressEvent)
25433     {
25434         if(nodeInfo instanceof Array){
25435             Roo.each(this.selections, function(s) {
25436                 this.unselect(s, nodeInfo);
25437             }, this);
25438             return;
25439         }
25440         var node = this.getNode(nodeInfo);
25441         if(!node || !this.isSelected(node)){
25442             //Roo.log("not selected");
25443             return; // not selected.
25444         }
25445         // fireevent???
25446         var ns = [];
25447         Roo.each(this.selections, function(s) {
25448             if (s == node ) {
25449                 Roo.fly(node).removeClass(this.selectedClass);
25450
25451                 return;
25452             }
25453             ns.push(s);
25454         },this);
25455         
25456         this.selections= ns;
25457         this.fireEvent("selectionchange", this, this.selections);
25458     },
25459
25460     /**
25461      * Gets a template node.
25462      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25463      * @return {HTMLElement} The node or null if it wasn't found
25464      */
25465     getNode : function(nodeInfo){
25466         if(typeof nodeInfo == "string"){
25467             return document.getElementById(nodeInfo);
25468         }else if(typeof nodeInfo == "number"){
25469             return this.nodes[nodeInfo];
25470         }
25471         return nodeInfo;
25472     },
25473
25474     /**
25475      * Gets a range template nodes.
25476      * @param {Number} startIndex
25477      * @param {Number} endIndex
25478      * @return {Array} An array of nodes
25479      */
25480     getNodes : function(start, end){
25481         var ns = this.nodes;
25482         start = start || 0;
25483         end = typeof end == "undefined" ? ns.length - 1 : end;
25484         var nodes = [];
25485         if(start <= end){
25486             for(var i = start; i <= end; i++){
25487                 nodes.push(ns[i]);
25488             }
25489         } else{
25490             for(var i = start; i >= end; i--){
25491                 nodes.push(ns[i]);
25492             }
25493         }
25494         return nodes;
25495     },
25496
25497     /**
25498      * Finds the index of the passed node
25499      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25500      * @return {Number} The index of the node or -1
25501      */
25502     indexOf : function(node){
25503         node = this.getNode(node);
25504         if(typeof node.nodeIndex == "number"){
25505             return node.nodeIndex;
25506         }
25507         var ns = this.nodes;
25508         for(var i = 0, len = ns.length; i < len; i++){
25509             if(ns[i] == node){
25510                 return i;
25511             }
25512         }
25513         return -1;
25514     }
25515 });
25516 /*
25517  * Based on:
25518  * Ext JS Library 1.1.1
25519  * Copyright(c) 2006-2007, Ext JS, LLC.
25520  *
25521  * Originally Released Under LGPL - original licence link has changed is not relivant.
25522  *
25523  * Fork - LGPL
25524  * <script type="text/javascript">
25525  */
25526
25527 /**
25528  * @class Roo.JsonView
25529  * @extends Roo.View
25530  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25531 <pre><code>
25532 var view = new Roo.JsonView({
25533     container: "my-element",
25534     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25535     multiSelect: true, 
25536     jsonRoot: "data" 
25537 });
25538
25539 // listen for node click?
25540 view.on("click", function(vw, index, node, e){
25541     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25542 });
25543
25544 // direct load of JSON data
25545 view.load("foobar.php");
25546
25547 // Example from my blog list
25548 var tpl = new Roo.Template(
25549     '&lt;div class="entry"&gt;' +
25550     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25551     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25552     "&lt;/div&gt;&lt;hr /&gt;"
25553 );
25554
25555 var moreView = new Roo.JsonView({
25556     container :  "entry-list", 
25557     template : tpl,
25558     jsonRoot: "posts"
25559 });
25560 moreView.on("beforerender", this.sortEntries, this);
25561 moreView.load({
25562     url: "/blog/get-posts.php",
25563     params: "allposts=true",
25564     text: "Loading Blog Entries..."
25565 });
25566 </code></pre>
25567
25568 * Note: old code is supported with arguments : (container, template, config)
25569
25570
25571  * @constructor
25572  * Create a new JsonView
25573  * 
25574  * @param {Object} config The config object
25575  * 
25576  */
25577 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25578     
25579     
25580     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25581
25582     var um = this.el.getUpdateManager();
25583     um.setRenderer(this);
25584     um.on("update", this.onLoad, this);
25585     um.on("failure", this.onLoadException, this);
25586
25587     /**
25588      * @event beforerender
25589      * Fires before rendering of the downloaded JSON data.
25590      * @param {Roo.JsonView} this
25591      * @param {Object} data The JSON data loaded
25592      */
25593     /**
25594      * @event load
25595      * Fires when data is loaded.
25596      * @param {Roo.JsonView} this
25597      * @param {Object} data The JSON data loaded
25598      * @param {Object} response The raw Connect response object
25599      */
25600     /**
25601      * @event loadexception
25602      * Fires when loading fails.
25603      * @param {Roo.JsonView} this
25604      * @param {Object} response The raw Connect response object
25605      */
25606     this.addEvents({
25607         'beforerender' : true,
25608         'load' : true,
25609         'loadexception' : true
25610     });
25611 };
25612 Roo.extend(Roo.JsonView, Roo.View, {
25613     /**
25614      * @type {String} The root property in the loaded JSON object that contains the data
25615      */
25616     jsonRoot : "",
25617
25618     /**
25619      * Refreshes the view.
25620      */
25621     refresh : function(){
25622         this.clearSelections();
25623         this.el.update("");
25624         var html = [];
25625         var o = this.jsonData;
25626         if(o && o.length > 0){
25627             for(var i = 0, len = o.length; i < len; i++){
25628                 var data = this.prepareData(o[i], i, o);
25629                 html[html.length] = this.tpl.apply(data);
25630             }
25631         }else{
25632             html.push(this.emptyText);
25633         }
25634         this.el.update(html.join(""));
25635         this.nodes = this.el.dom.childNodes;
25636         this.updateIndexes(0);
25637     },
25638
25639     /**
25640      * 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.
25641      * @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:
25642      <pre><code>
25643      view.load({
25644          url: "your-url.php",
25645          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25646          callback: yourFunction,
25647          scope: yourObject, //(optional scope)
25648          discardUrl: false,
25649          nocache: false,
25650          text: "Loading...",
25651          timeout: 30,
25652          scripts: false
25653      });
25654      </code></pre>
25655      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25656      * 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.
25657      * @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}
25658      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25659      * @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.
25660      */
25661     load : function(){
25662         var um = this.el.getUpdateManager();
25663         um.update.apply(um, arguments);
25664     },
25665
25666     render : function(el, response){
25667         this.clearSelections();
25668         this.el.update("");
25669         var o;
25670         try{
25671             o = Roo.util.JSON.decode(response.responseText);
25672             if(this.jsonRoot){
25673                 
25674                 o = o[this.jsonRoot];
25675             }
25676         } catch(e){
25677         }
25678         /**
25679          * The current JSON data or null
25680          */
25681         this.jsonData = o;
25682         this.beforeRender();
25683         this.refresh();
25684     },
25685
25686 /**
25687  * Get the number of records in the current JSON dataset
25688  * @return {Number}
25689  */
25690     getCount : function(){
25691         return this.jsonData ? this.jsonData.length : 0;
25692     },
25693
25694 /**
25695  * Returns the JSON object for the specified node(s)
25696  * @param {HTMLElement/Array} node The node or an array of nodes
25697  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25698  * you get the JSON object for the node
25699  */
25700     getNodeData : function(node){
25701         if(node instanceof Array){
25702             var data = [];
25703             for(var i = 0, len = node.length; i < len; i++){
25704                 data.push(this.getNodeData(node[i]));
25705             }
25706             return data;
25707         }
25708         return this.jsonData[this.indexOf(node)] || null;
25709     },
25710
25711     beforeRender : function(){
25712         this.snapshot = this.jsonData;
25713         if(this.sortInfo){
25714             this.sort.apply(this, this.sortInfo);
25715         }
25716         this.fireEvent("beforerender", this, this.jsonData);
25717     },
25718
25719     onLoad : function(el, o){
25720         this.fireEvent("load", this, this.jsonData, o);
25721     },
25722
25723     onLoadException : function(el, o){
25724         this.fireEvent("loadexception", this, o);
25725     },
25726
25727 /**
25728  * Filter the data by a specific property.
25729  * @param {String} property A property on your JSON objects
25730  * @param {String/RegExp} value Either string that the property values
25731  * should start with, or a RegExp to test against the property
25732  */
25733     filter : function(property, value){
25734         if(this.jsonData){
25735             var data = [];
25736             var ss = this.snapshot;
25737             if(typeof value == "string"){
25738                 var vlen = value.length;
25739                 if(vlen == 0){
25740                     this.clearFilter();
25741                     return;
25742                 }
25743                 value = value.toLowerCase();
25744                 for(var i = 0, len = ss.length; i < len; i++){
25745                     var o = ss[i];
25746                     if(o[property].substr(0, vlen).toLowerCase() == value){
25747                         data.push(o);
25748                     }
25749                 }
25750             } else if(value.exec){ // regex?
25751                 for(var i = 0, len = ss.length; i < len; i++){
25752                     var o = ss[i];
25753                     if(value.test(o[property])){
25754                         data.push(o);
25755                     }
25756                 }
25757             } else{
25758                 return;
25759             }
25760             this.jsonData = data;
25761             this.refresh();
25762         }
25763     },
25764
25765 /**
25766  * Filter by a function. The passed function will be called with each
25767  * object in the current dataset. If the function returns true the value is kept,
25768  * otherwise it is filtered.
25769  * @param {Function} fn
25770  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25771  */
25772     filterBy : function(fn, scope){
25773         if(this.jsonData){
25774             var data = [];
25775             var ss = this.snapshot;
25776             for(var i = 0, len = ss.length; i < len; i++){
25777                 var o = ss[i];
25778                 if(fn.call(scope || this, o)){
25779                     data.push(o);
25780                 }
25781             }
25782             this.jsonData = data;
25783             this.refresh();
25784         }
25785     },
25786
25787 /**
25788  * Clears the current filter.
25789  */
25790     clearFilter : function(){
25791         if(this.snapshot && this.jsonData != this.snapshot){
25792             this.jsonData = this.snapshot;
25793             this.refresh();
25794         }
25795     },
25796
25797
25798 /**
25799  * Sorts the data for this view and refreshes it.
25800  * @param {String} property A property on your JSON objects to sort on
25801  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25802  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25803  */
25804     sort : function(property, dir, sortType){
25805         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25806         if(this.jsonData){
25807             var p = property;
25808             var dsc = dir && dir.toLowerCase() == "desc";
25809             var f = function(o1, o2){
25810                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25811                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25812                 ;
25813                 if(v1 < v2){
25814                     return dsc ? +1 : -1;
25815                 } else if(v1 > v2){
25816                     return dsc ? -1 : +1;
25817                 } else{
25818                     return 0;
25819                 }
25820             };
25821             this.jsonData.sort(f);
25822             this.refresh();
25823             if(this.jsonData != this.snapshot){
25824                 this.snapshot.sort(f);
25825             }
25826         }
25827     }
25828 });/*
25829  * Based on:
25830  * Ext JS Library 1.1.1
25831  * Copyright(c) 2006-2007, Ext JS, LLC.
25832  *
25833  * Originally Released Under LGPL - original licence link has changed is not relivant.
25834  *
25835  * Fork - LGPL
25836  * <script type="text/javascript">
25837  */
25838  
25839
25840 /**
25841  * @class Roo.ColorPalette
25842  * @extends Roo.Component
25843  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25844  * Here's an example of typical usage:
25845  * <pre><code>
25846 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25847 cp.render('my-div');
25848
25849 cp.on('select', function(palette, selColor){
25850     // do something with selColor
25851 });
25852 </code></pre>
25853  * @constructor
25854  * Create a new ColorPalette
25855  * @param {Object} config The config object
25856  */
25857 Roo.ColorPalette = function(config){
25858     Roo.ColorPalette.superclass.constructor.call(this, config);
25859     this.addEvents({
25860         /**
25861              * @event select
25862              * Fires when a color is selected
25863              * @param {ColorPalette} this
25864              * @param {String} color The 6-digit color hex code (without the # symbol)
25865              */
25866         select: true
25867     });
25868
25869     if(this.handler){
25870         this.on("select", this.handler, this.scope, true);
25871     }
25872 };
25873 Roo.extend(Roo.ColorPalette, Roo.Component, {
25874     /**
25875      * @cfg {String} itemCls
25876      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25877      */
25878     itemCls : "x-color-palette",
25879     /**
25880      * @cfg {String} value
25881      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25882      * the hex codes are case-sensitive.
25883      */
25884     value : null,
25885     clickEvent:'click',
25886     // private
25887     ctype: "Roo.ColorPalette",
25888
25889     /**
25890      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25891      */
25892     allowReselect : false,
25893
25894     /**
25895      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25896      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25897      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25898      * of colors with the width setting until the box is symmetrical.</p>
25899      * <p>You can override individual colors if needed:</p>
25900      * <pre><code>
25901 var cp = new Roo.ColorPalette();
25902 cp.colors[0] = "FF0000";  // change the first box to red
25903 </code></pre>
25904
25905 Or you can provide a custom array of your own for complete control:
25906 <pre><code>
25907 var cp = new Roo.ColorPalette();
25908 cp.colors = ["000000", "993300", "333300"];
25909 </code></pre>
25910      * @type Array
25911      */
25912     colors : [
25913         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25914         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25915         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25916         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25917         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25918     ],
25919
25920     // private
25921     onRender : function(container, position){
25922         var t = new Roo.MasterTemplate(
25923             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25924         );
25925         var c = this.colors;
25926         for(var i = 0, len = c.length; i < len; i++){
25927             t.add([c[i]]);
25928         }
25929         var el = document.createElement("div");
25930         el.className = this.itemCls;
25931         t.overwrite(el);
25932         container.dom.insertBefore(el, position);
25933         this.el = Roo.get(el);
25934         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25935         if(this.clickEvent != 'click'){
25936             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25937         }
25938     },
25939
25940     // private
25941     afterRender : function(){
25942         Roo.ColorPalette.superclass.afterRender.call(this);
25943         if(this.value){
25944             var s = this.value;
25945             this.value = null;
25946             this.select(s);
25947         }
25948     },
25949
25950     // private
25951     handleClick : function(e, t){
25952         e.preventDefault();
25953         if(!this.disabled){
25954             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25955             this.select(c.toUpperCase());
25956         }
25957     },
25958
25959     /**
25960      * Selects the specified color in the palette (fires the select event)
25961      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25962      */
25963     select : function(color){
25964         color = color.replace("#", "");
25965         if(color != this.value || this.allowReselect){
25966             var el = this.el;
25967             if(this.value){
25968                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25969             }
25970             el.child("a.color-"+color).addClass("x-color-palette-sel");
25971             this.value = color;
25972             this.fireEvent("select", this, color);
25973         }
25974     }
25975 });/*
25976  * Based on:
25977  * Ext JS Library 1.1.1
25978  * Copyright(c) 2006-2007, Ext JS, LLC.
25979  *
25980  * Originally Released Under LGPL - original licence link has changed is not relivant.
25981  *
25982  * Fork - LGPL
25983  * <script type="text/javascript">
25984  */
25985  
25986 /**
25987  * @class Roo.DatePicker
25988  * @extends Roo.Component
25989  * Simple date picker class.
25990  * @constructor
25991  * Create a new DatePicker
25992  * @param {Object} config The config object
25993  */
25994 Roo.DatePicker = function(config){
25995     Roo.DatePicker.superclass.constructor.call(this, config);
25996
25997     this.value = config && config.value ?
25998                  config.value.clearTime() : new Date().clearTime();
25999
26000     this.addEvents({
26001         /**
26002              * @event select
26003              * Fires when a date is selected
26004              * @param {DatePicker} this
26005              * @param {Date} date The selected date
26006              */
26007         'select': true,
26008         /**
26009              * @event monthchange
26010              * Fires when the displayed month changes 
26011              * @param {DatePicker} this
26012              * @param {Date} date The selected month
26013              */
26014         'monthchange': true
26015     });
26016
26017     if(this.handler){
26018         this.on("select", this.handler,  this.scope || this);
26019     }
26020     // build the disabledDatesRE
26021     if(!this.disabledDatesRE && this.disabledDates){
26022         var dd = this.disabledDates;
26023         var re = "(?:";
26024         for(var i = 0; i < dd.length; i++){
26025             re += dd[i];
26026             if(i != dd.length-1) re += "|";
26027         }
26028         this.disabledDatesRE = new RegExp(re + ")");
26029     }
26030 };
26031
26032 Roo.extend(Roo.DatePicker, Roo.Component, {
26033     /**
26034      * @cfg {String} todayText
26035      * The text to display on the button that selects the current date (defaults to "Today")
26036      */
26037     todayText : "Today",
26038     /**
26039      * @cfg {String} okText
26040      * The text to display on the ok button
26041      */
26042     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26043     /**
26044      * @cfg {String} cancelText
26045      * The text to display on the cancel button
26046      */
26047     cancelText : "Cancel",
26048     /**
26049      * @cfg {String} todayTip
26050      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26051      */
26052     todayTip : "{0} (Spacebar)",
26053     /**
26054      * @cfg {Date} minDate
26055      * Minimum allowable date (JavaScript date object, defaults to null)
26056      */
26057     minDate : null,
26058     /**
26059      * @cfg {Date} maxDate
26060      * Maximum allowable date (JavaScript date object, defaults to null)
26061      */
26062     maxDate : null,
26063     /**
26064      * @cfg {String} minText
26065      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26066      */
26067     minText : "This date is before the minimum date",
26068     /**
26069      * @cfg {String} maxText
26070      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26071      */
26072     maxText : "This date is after the maximum date",
26073     /**
26074      * @cfg {String} format
26075      * The default date format string which can be overriden for localization support.  The format must be
26076      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26077      */
26078     format : "m/d/y",
26079     /**
26080      * @cfg {Array} disabledDays
26081      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26082      */
26083     disabledDays : null,
26084     /**
26085      * @cfg {String} disabledDaysText
26086      * The tooltip to display when the date falls on a disabled day (defaults to "")
26087      */
26088     disabledDaysText : "",
26089     /**
26090      * @cfg {RegExp} disabledDatesRE
26091      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26092      */
26093     disabledDatesRE : null,
26094     /**
26095      * @cfg {String} disabledDatesText
26096      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26097      */
26098     disabledDatesText : "",
26099     /**
26100      * @cfg {Boolean} constrainToViewport
26101      * True to constrain the date picker to the viewport (defaults to true)
26102      */
26103     constrainToViewport : true,
26104     /**
26105      * @cfg {Array} monthNames
26106      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26107      */
26108     monthNames : Date.monthNames,
26109     /**
26110      * @cfg {Array} dayNames
26111      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26112      */
26113     dayNames : Date.dayNames,
26114     /**
26115      * @cfg {String} nextText
26116      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26117      */
26118     nextText: 'Next Month (Control+Right)',
26119     /**
26120      * @cfg {String} prevText
26121      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26122      */
26123     prevText: 'Previous Month (Control+Left)',
26124     /**
26125      * @cfg {String} monthYearText
26126      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26127      */
26128     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26129     /**
26130      * @cfg {Number} startDay
26131      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26132      */
26133     startDay : 0,
26134     /**
26135      * @cfg {Bool} showClear
26136      * Show a clear button (usefull for date form elements that can be blank.)
26137      */
26138     
26139     showClear: false,
26140     
26141     /**
26142      * Sets the value of the date field
26143      * @param {Date} value The date to set
26144      */
26145     setValue : function(value){
26146         var old = this.value;
26147         
26148         if (typeof(value) == 'string') {
26149          
26150             value = Date.parseDate(value, this.format);
26151         }
26152         if (!value) {
26153             value = new Date();
26154         }
26155         
26156         this.value = value.clearTime(true);
26157         if(this.el){
26158             this.update(this.value);
26159         }
26160     },
26161
26162     /**
26163      * Gets the current selected value of the date field
26164      * @return {Date} The selected date
26165      */
26166     getValue : function(){
26167         return this.value;
26168     },
26169
26170     // private
26171     focus : function(){
26172         if(this.el){
26173             this.update(this.activeDate);
26174         }
26175     },
26176
26177     // privateval
26178     onRender : function(container, position){
26179         
26180         var m = [
26181              '<table cellspacing="0">',
26182                 '<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>',
26183                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26184         var dn = this.dayNames;
26185         for(var i = 0; i < 7; i++){
26186             var d = this.startDay+i;
26187             if(d > 6){
26188                 d = d-7;
26189             }
26190             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26191         }
26192         m[m.length] = "</tr></thead><tbody><tr>";
26193         for(var i = 0; i < 42; i++) {
26194             if(i % 7 == 0 && i != 0){
26195                 m[m.length] = "</tr><tr>";
26196             }
26197             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26198         }
26199         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26200             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26201
26202         var el = document.createElement("div");
26203         el.className = "x-date-picker";
26204         el.innerHTML = m.join("");
26205
26206         container.dom.insertBefore(el, position);
26207
26208         this.el = Roo.get(el);
26209         this.eventEl = Roo.get(el.firstChild);
26210
26211         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26212             handler: this.showPrevMonth,
26213             scope: this,
26214             preventDefault:true,
26215             stopDefault:true
26216         });
26217
26218         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26219             handler: this.showNextMonth,
26220             scope: this,
26221             preventDefault:true,
26222             stopDefault:true
26223         });
26224
26225         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26226
26227         this.monthPicker = this.el.down('div.x-date-mp');
26228         this.monthPicker.enableDisplayMode('block');
26229         
26230         var kn = new Roo.KeyNav(this.eventEl, {
26231             "left" : function(e){
26232                 e.ctrlKey ?
26233                     this.showPrevMonth() :
26234                     this.update(this.activeDate.add("d", -1));
26235             },
26236
26237             "right" : function(e){
26238                 e.ctrlKey ?
26239                     this.showNextMonth() :
26240                     this.update(this.activeDate.add("d", 1));
26241             },
26242
26243             "up" : function(e){
26244                 e.ctrlKey ?
26245                     this.showNextYear() :
26246                     this.update(this.activeDate.add("d", -7));
26247             },
26248
26249             "down" : function(e){
26250                 e.ctrlKey ?
26251                     this.showPrevYear() :
26252                     this.update(this.activeDate.add("d", 7));
26253             },
26254
26255             "pageUp" : function(e){
26256                 this.showNextMonth();
26257             },
26258
26259             "pageDown" : function(e){
26260                 this.showPrevMonth();
26261             },
26262
26263             "enter" : function(e){
26264                 e.stopPropagation();
26265                 return true;
26266             },
26267
26268             scope : this
26269         });
26270
26271         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26272
26273         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26274
26275         this.el.unselectable();
26276         
26277         this.cells = this.el.select("table.x-date-inner tbody td");
26278         this.textNodes = this.el.query("table.x-date-inner tbody span");
26279
26280         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26281             text: "&#160;",
26282             tooltip: this.monthYearText
26283         });
26284
26285         this.mbtn.on('click', this.showMonthPicker, this);
26286         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26287
26288
26289         var today = (new Date()).dateFormat(this.format);
26290         
26291         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26292         if (this.showClear) {
26293             baseTb.add( new Roo.Toolbar.Fill());
26294         }
26295         baseTb.add({
26296             text: String.format(this.todayText, today),
26297             tooltip: String.format(this.todayTip, today),
26298             handler: this.selectToday,
26299             scope: this
26300         });
26301         
26302         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26303             
26304         //});
26305         if (this.showClear) {
26306             
26307             baseTb.add( new Roo.Toolbar.Fill());
26308             baseTb.add({
26309                 text: '&#160;',
26310                 cls: 'x-btn-icon x-btn-clear',
26311                 handler: function() {
26312                     //this.value = '';
26313                     this.fireEvent("select", this, '');
26314                 },
26315                 scope: this
26316             });
26317         }
26318         
26319         
26320         if(Roo.isIE){
26321             this.el.repaint();
26322         }
26323         this.update(this.value);
26324     },
26325
26326     createMonthPicker : function(){
26327         if(!this.monthPicker.dom.firstChild){
26328             var buf = ['<table border="0" cellspacing="0">'];
26329             for(var i = 0; i < 6; i++){
26330                 buf.push(
26331                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26332                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26333                     i == 0 ?
26334                     '<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>' :
26335                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26336                 );
26337             }
26338             buf.push(
26339                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26340                     this.okText,
26341                     '</button><button type="button" class="x-date-mp-cancel">',
26342                     this.cancelText,
26343                     '</button></td></tr>',
26344                 '</table>'
26345             );
26346             this.monthPicker.update(buf.join(''));
26347             this.monthPicker.on('click', this.onMonthClick, this);
26348             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26349
26350             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26351             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26352
26353             this.mpMonths.each(function(m, a, i){
26354                 i += 1;
26355                 if((i%2) == 0){
26356                     m.dom.xmonth = 5 + Math.round(i * .5);
26357                 }else{
26358                     m.dom.xmonth = Math.round((i-1) * .5);
26359                 }
26360             });
26361         }
26362     },
26363
26364     showMonthPicker : function(){
26365         this.createMonthPicker();
26366         var size = this.el.getSize();
26367         this.monthPicker.setSize(size);
26368         this.monthPicker.child('table').setSize(size);
26369
26370         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26371         this.updateMPMonth(this.mpSelMonth);
26372         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26373         this.updateMPYear(this.mpSelYear);
26374
26375         this.monthPicker.slideIn('t', {duration:.2});
26376     },
26377
26378     updateMPYear : function(y){
26379         this.mpyear = y;
26380         var ys = this.mpYears.elements;
26381         for(var i = 1; i <= 10; i++){
26382             var td = ys[i-1], y2;
26383             if((i%2) == 0){
26384                 y2 = y + Math.round(i * .5);
26385                 td.firstChild.innerHTML = y2;
26386                 td.xyear = y2;
26387             }else{
26388                 y2 = y - (5-Math.round(i * .5));
26389                 td.firstChild.innerHTML = y2;
26390                 td.xyear = y2;
26391             }
26392             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26393         }
26394     },
26395
26396     updateMPMonth : function(sm){
26397         this.mpMonths.each(function(m, a, i){
26398             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26399         });
26400     },
26401
26402     selectMPMonth: function(m){
26403         
26404     },
26405
26406     onMonthClick : function(e, t){
26407         e.stopEvent();
26408         var el = new Roo.Element(t), pn;
26409         if(el.is('button.x-date-mp-cancel')){
26410             this.hideMonthPicker();
26411         }
26412         else if(el.is('button.x-date-mp-ok')){
26413             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26414             this.hideMonthPicker();
26415         }
26416         else if(pn = el.up('td.x-date-mp-month', 2)){
26417             this.mpMonths.removeClass('x-date-mp-sel');
26418             pn.addClass('x-date-mp-sel');
26419             this.mpSelMonth = pn.dom.xmonth;
26420         }
26421         else if(pn = el.up('td.x-date-mp-year', 2)){
26422             this.mpYears.removeClass('x-date-mp-sel');
26423             pn.addClass('x-date-mp-sel');
26424             this.mpSelYear = pn.dom.xyear;
26425         }
26426         else if(el.is('a.x-date-mp-prev')){
26427             this.updateMPYear(this.mpyear-10);
26428         }
26429         else if(el.is('a.x-date-mp-next')){
26430             this.updateMPYear(this.mpyear+10);
26431         }
26432     },
26433
26434     onMonthDblClick : function(e, t){
26435         e.stopEvent();
26436         var el = new Roo.Element(t), pn;
26437         if(pn = el.up('td.x-date-mp-month', 2)){
26438             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26439             this.hideMonthPicker();
26440         }
26441         else if(pn = el.up('td.x-date-mp-year', 2)){
26442             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26443             this.hideMonthPicker();
26444         }
26445     },
26446
26447     hideMonthPicker : function(disableAnim){
26448         if(this.monthPicker){
26449             if(disableAnim === true){
26450                 this.monthPicker.hide();
26451             }else{
26452                 this.monthPicker.slideOut('t', {duration:.2});
26453             }
26454         }
26455     },
26456
26457     // private
26458     showPrevMonth : function(e){
26459         this.update(this.activeDate.add("mo", -1));
26460     },
26461
26462     // private
26463     showNextMonth : function(e){
26464         this.update(this.activeDate.add("mo", 1));
26465     },
26466
26467     // private
26468     showPrevYear : function(){
26469         this.update(this.activeDate.add("y", -1));
26470     },
26471
26472     // private
26473     showNextYear : function(){
26474         this.update(this.activeDate.add("y", 1));
26475     },
26476
26477     // private
26478     handleMouseWheel : function(e){
26479         var delta = e.getWheelDelta();
26480         if(delta > 0){
26481             this.showPrevMonth();
26482             e.stopEvent();
26483         } else if(delta < 0){
26484             this.showNextMonth();
26485             e.stopEvent();
26486         }
26487     },
26488
26489     // private
26490     handleDateClick : function(e, t){
26491         e.stopEvent();
26492         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26493             this.setValue(new Date(t.dateValue));
26494             this.fireEvent("select", this, this.value);
26495         }
26496     },
26497
26498     // private
26499     selectToday : function(){
26500         this.setValue(new Date().clearTime());
26501         this.fireEvent("select", this, this.value);
26502     },
26503
26504     // private
26505     update : function(date)
26506     {
26507         var vd = this.activeDate;
26508         this.activeDate = date;
26509         if(vd && this.el){
26510             var t = date.getTime();
26511             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26512                 this.cells.removeClass("x-date-selected");
26513                 this.cells.each(function(c){
26514                    if(c.dom.firstChild.dateValue == t){
26515                        c.addClass("x-date-selected");
26516                        setTimeout(function(){
26517                             try{c.dom.firstChild.focus();}catch(e){}
26518                        }, 50);
26519                        return false;
26520                    }
26521                 });
26522                 return;
26523             }
26524         }
26525         
26526         var days = date.getDaysInMonth();
26527         var firstOfMonth = date.getFirstDateOfMonth();
26528         var startingPos = firstOfMonth.getDay()-this.startDay;
26529
26530         if(startingPos <= this.startDay){
26531             startingPos += 7;
26532         }
26533
26534         var pm = date.add("mo", -1);
26535         var prevStart = pm.getDaysInMonth()-startingPos;
26536
26537         var cells = this.cells.elements;
26538         var textEls = this.textNodes;
26539         days += startingPos;
26540
26541         // convert everything to numbers so it's fast
26542         var day = 86400000;
26543         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26544         var today = new Date().clearTime().getTime();
26545         var sel = date.clearTime().getTime();
26546         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26547         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26548         var ddMatch = this.disabledDatesRE;
26549         var ddText = this.disabledDatesText;
26550         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26551         var ddaysText = this.disabledDaysText;
26552         var format = this.format;
26553
26554         var setCellClass = function(cal, cell){
26555             cell.title = "";
26556             var t = d.getTime();
26557             cell.firstChild.dateValue = t;
26558             if(t == today){
26559                 cell.className += " x-date-today";
26560                 cell.title = cal.todayText;
26561             }
26562             if(t == sel){
26563                 cell.className += " x-date-selected";
26564                 setTimeout(function(){
26565                     try{cell.firstChild.focus();}catch(e){}
26566                 }, 50);
26567             }
26568             // disabling
26569             if(t < min) {
26570                 cell.className = " x-date-disabled";
26571                 cell.title = cal.minText;
26572                 return;
26573             }
26574             if(t > max) {
26575                 cell.className = " x-date-disabled";
26576                 cell.title = cal.maxText;
26577                 return;
26578             }
26579             if(ddays){
26580                 if(ddays.indexOf(d.getDay()) != -1){
26581                     cell.title = ddaysText;
26582                     cell.className = " x-date-disabled";
26583                 }
26584             }
26585             if(ddMatch && format){
26586                 var fvalue = d.dateFormat(format);
26587                 if(ddMatch.test(fvalue)){
26588                     cell.title = ddText.replace("%0", fvalue);
26589                     cell.className = " x-date-disabled";
26590                 }
26591             }
26592         };
26593
26594         var i = 0;
26595         for(; i < startingPos; i++) {
26596             textEls[i].innerHTML = (++prevStart);
26597             d.setDate(d.getDate()+1);
26598             cells[i].className = "x-date-prevday";
26599             setCellClass(this, cells[i]);
26600         }
26601         for(; i < days; i++){
26602             intDay = i - startingPos + 1;
26603             textEls[i].innerHTML = (intDay);
26604             d.setDate(d.getDate()+1);
26605             cells[i].className = "x-date-active";
26606             setCellClass(this, cells[i]);
26607         }
26608         var extraDays = 0;
26609         for(; i < 42; i++) {
26610              textEls[i].innerHTML = (++extraDays);
26611              d.setDate(d.getDate()+1);
26612              cells[i].className = "x-date-nextday";
26613              setCellClass(this, cells[i]);
26614         }
26615
26616         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26617         this.fireEvent('monthchange', this, date);
26618         
26619         if(!this.internalRender){
26620             var main = this.el.dom.firstChild;
26621             var w = main.offsetWidth;
26622             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26623             Roo.fly(main).setWidth(w);
26624             this.internalRender = true;
26625             // opera does not respect the auto grow header center column
26626             // then, after it gets a width opera refuses to recalculate
26627             // without a second pass
26628             if(Roo.isOpera && !this.secondPass){
26629                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26630                 this.secondPass = true;
26631                 this.update.defer(10, this, [date]);
26632             }
26633         }
26634         
26635         
26636     }
26637 });        /*
26638  * Based on:
26639  * Ext JS Library 1.1.1
26640  * Copyright(c) 2006-2007, Ext JS, LLC.
26641  *
26642  * Originally Released Under LGPL - original licence link has changed is not relivant.
26643  *
26644  * Fork - LGPL
26645  * <script type="text/javascript">
26646  */
26647 /**
26648  * @class Roo.TabPanel
26649  * @extends Roo.util.Observable
26650  * A lightweight tab container.
26651  * <br><br>
26652  * Usage:
26653  * <pre><code>
26654 // basic tabs 1, built from existing content
26655 var tabs = new Roo.TabPanel("tabs1");
26656 tabs.addTab("script", "View Script");
26657 tabs.addTab("markup", "View Markup");
26658 tabs.activate("script");
26659
26660 // more advanced tabs, built from javascript
26661 var jtabs = new Roo.TabPanel("jtabs");
26662 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26663
26664 // set up the UpdateManager
26665 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26666 var updater = tab2.getUpdateManager();
26667 updater.setDefaultUrl("ajax1.htm");
26668 tab2.on('activate', updater.refresh, updater, true);
26669
26670 // Use setUrl for Ajax loading
26671 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26672 tab3.setUrl("ajax2.htm", null, true);
26673
26674 // Disabled tab
26675 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26676 tab4.disable();
26677
26678 jtabs.activate("jtabs-1");
26679  * </code></pre>
26680  * @constructor
26681  * Create a new TabPanel.
26682  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26683  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26684  */
26685 Roo.TabPanel = function(container, config){
26686     /**
26687     * The container element for this TabPanel.
26688     * @type Roo.Element
26689     */
26690     this.el = Roo.get(container, true);
26691     if(config){
26692         if(typeof config == "boolean"){
26693             this.tabPosition = config ? "bottom" : "top";
26694         }else{
26695             Roo.apply(this, config);
26696         }
26697     }
26698     if(this.tabPosition == "bottom"){
26699         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26700         this.el.addClass("x-tabs-bottom");
26701     }
26702     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26703     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26704     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26705     if(Roo.isIE){
26706         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26707     }
26708     if(this.tabPosition != "bottom"){
26709         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26710          * @type Roo.Element
26711          */
26712         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26713         this.el.addClass("x-tabs-top");
26714     }
26715     this.items = [];
26716
26717     this.bodyEl.setStyle("position", "relative");
26718
26719     this.active = null;
26720     this.activateDelegate = this.activate.createDelegate(this);
26721
26722     this.addEvents({
26723         /**
26724          * @event tabchange
26725          * Fires when the active tab changes
26726          * @param {Roo.TabPanel} this
26727          * @param {Roo.TabPanelItem} activePanel The new active tab
26728          */
26729         "tabchange": true,
26730         /**
26731          * @event beforetabchange
26732          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26733          * @param {Roo.TabPanel} this
26734          * @param {Object} e Set cancel to true on this object to cancel the tab change
26735          * @param {Roo.TabPanelItem} tab The tab being changed to
26736          */
26737         "beforetabchange" : true
26738     });
26739
26740     Roo.EventManager.onWindowResize(this.onResize, this);
26741     this.cpad = this.el.getPadding("lr");
26742     this.hiddenCount = 0;
26743
26744
26745     // toolbar on the tabbar support...
26746     if (this.toolbar) {
26747         var tcfg = this.toolbar;
26748         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26749         this.toolbar = new Roo.Toolbar(tcfg);
26750         if (Roo.isSafari) {
26751             var tbl = tcfg.container.child('table', true);
26752             tbl.setAttribute('width', '100%');
26753         }
26754         
26755     }
26756    
26757
26758
26759     Roo.TabPanel.superclass.constructor.call(this);
26760 };
26761
26762 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26763     /*
26764      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26765      */
26766     tabPosition : "top",
26767     /*
26768      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26769      */
26770     currentTabWidth : 0,
26771     /*
26772      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26773      */
26774     minTabWidth : 40,
26775     /*
26776      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26777      */
26778     maxTabWidth : 250,
26779     /*
26780      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26781      */
26782     preferredTabWidth : 175,
26783     /*
26784      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26785      */
26786     resizeTabs : false,
26787     /*
26788      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26789      */
26790     monitorResize : true,
26791     /*
26792      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26793      */
26794     toolbar : false,
26795
26796     /**
26797      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26798      * @param {String} id The id of the div to use <b>or create</b>
26799      * @param {String} text The text for the tab
26800      * @param {String} content (optional) Content to put in the TabPanelItem body
26801      * @param {Boolean} closable (optional) True to create a close icon on the tab
26802      * @return {Roo.TabPanelItem} The created TabPanelItem
26803      */
26804     addTab : function(id, text, content, closable){
26805         var item = new Roo.TabPanelItem(this, id, text, closable);
26806         this.addTabItem(item);
26807         if(content){
26808             item.setContent(content);
26809         }
26810         return item;
26811     },
26812
26813     /**
26814      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26815      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26816      * @return {Roo.TabPanelItem}
26817      */
26818     getTab : function(id){
26819         return this.items[id];
26820     },
26821
26822     /**
26823      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26824      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26825      */
26826     hideTab : function(id){
26827         var t = this.items[id];
26828         if(!t.isHidden()){
26829            t.setHidden(true);
26830            this.hiddenCount++;
26831            this.autoSizeTabs();
26832         }
26833     },
26834
26835     /**
26836      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26837      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26838      */
26839     unhideTab : function(id){
26840         var t = this.items[id];
26841         if(t.isHidden()){
26842            t.setHidden(false);
26843            this.hiddenCount--;
26844            this.autoSizeTabs();
26845         }
26846     },
26847
26848     /**
26849      * Adds an existing {@link Roo.TabPanelItem}.
26850      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26851      */
26852     addTabItem : function(item){
26853         this.items[item.id] = item;
26854         this.items.push(item);
26855         if(this.resizeTabs){
26856            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26857            this.autoSizeTabs();
26858         }else{
26859             item.autoSize();
26860         }
26861     },
26862
26863     /**
26864      * Removes a {@link Roo.TabPanelItem}.
26865      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26866      */
26867     removeTab : function(id){
26868         var items = this.items;
26869         var tab = items[id];
26870         if(!tab) { return; }
26871         var index = items.indexOf(tab);
26872         if(this.active == tab && items.length > 1){
26873             var newTab = this.getNextAvailable(index);
26874             if(newTab) {
26875                 newTab.activate();
26876             }
26877         }
26878         this.stripEl.dom.removeChild(tab.pnode.dom);
26879         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26880             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26881         }
26882         items.splice(index, 1);
26883         delete this.items[tab.id];
26884         tab.fireEvent("close", tab);
26885         tab.purgeListeners();
26886         this.autoSizeTabs();
26887     },
26888
26889     getNextAvailable : function(start){
26890         var items = this.items;
26891         var index = start;
26892         // look for a next tab that will slide over to
26893         // replace the one being removed
26894         while(index < items.length){
26895             var item = items[++index];
26896             if(item && !item.isHidden()){
26897                 return item;
26898             }
26899         }
26900         // if one isn't found select the previous tab (on the left)
26901         index = start;
26902         while(index >= 0){
26903             var item = items[--index];
26904             if(item && !item.isHidden()){
26905                 return item;
26906             }
26907         }
26908         return null;
26909     },
26910
26911     /**
26912      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26913      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26914      */
26915     disableTab : function(id){
26916         var tab = this.items[id];
26917         if(tab && this.active != tab){
26918             tab.disable();
26919         }
26920     },
26921
26922     /**
26923      * Enables a {@link Roo.TabPanelItem} that is disabled.
26924      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26925      */
26926     enableTab : function(id){
26927         var tab = this.items[id];
26928         tab.enable();
26929     },
26930
26931     /**
26932      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26933      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26934      * @return {Roo.TabPanelItem} The TabPanelItem.
26935      */
26936     activate : function(id){
26937         var tab = this.items[id];
26938         if(!tab){
26939             return null;
26940         }
26941         if(tab == this.active || tab.disabled){
26942             return tab;
26943         }
26944         var e = {};
26945         this.fireEvent("beforetabchange", this, e, tab);
26946         if(e.cancel !== true && !tab.disabled){
26947             if(this.active){
26948                 this.active.hide();
26949             }
26950             this.active = this.items[id];
26951             this.active.show();
26952             this.fireEvent("tabchange", this, this.active);
26953         }
26954         return tab;
26955     },
26956
26957     /**
26958      * Gets the active {@link Roo.TabPanelItem}.
26959      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26960      */
26961     getActiveTab : function(){
26962         return this.active;
26963     },
26964
26965     /**
26966      * Updates the tab body element to fit the height of the container element
26967      * for overflow scrolling
26968      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26969      */
26970     syncHeight : function(targetHeight){
26971         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26972         var bm = this.bodyEl.getMargins();
26973         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26974         this.bodyEl.setHeight(newHeight);
26975         return newHeight;
26976     },
26977
26978     onResize : function(){
26979         if(this.monitorResize){
26980             this.autoSizeTabs();
26981         }
26982     },
26983
26984     /**
26985      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26986      */
26987     beginUpdate : function(){
26988         this.updating = true;
26989     },
26990
26991     /**
26992      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26993      */
26994     endUpdate : function(){
26995         this.updating = false;
26996         this.autoSizeTabs();
26997     },
26998
26999     /**
27000      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27001      */
27002     autoSizeTabs : function(){
27003         var count = this.items.length;
27004         var vcount = count - this.hiddenCount;
27005         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27006         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27007         var availWidth = Math.floor(w / vcount);
27008         var b = this.stripBody;
27009         if(b.getWidth() > w){
27010             var tabs = this.items;
27011             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27012             if(availWidth < this.minTabWidth){
27013                 /*if(!this.sleft){    // incomplete scrolling code
27014                     this.createScrollButtons();
27015                 }
27016                 this.showScroll();
27017                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27018             }
27019         }else{
27020             if(this.currentTabWidth < this.preferredTabWidth){
27021                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27022             }
27023         }
27024     },
27025
27026     /**
27027      * Returns the number of tabs in this TabPanel.
27028      * @return {Number}
27029      */
27030      getCount : function(){
27031          return this.items.length;
27032      },
27033
27034     /**
27035      * Resizes all the tabs to the passed width
27036      * @param {Number} The new width
27037      */
27038     setTabWidth : function(width){
27039         this.currentTabWidth = width;
27040         for(var i = 0, len = this.items.length; i < len; i++) {
27041                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27042         }
27043     },
27044
27045     /**
27046      * Destroys this TabPanel
27047      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27048      */
27049     destroy : function(removeEl){
27050         Roo.EventManager.removeResizeListener(this.onResize, this);
27051         for(var i = 0, len = this.items.length; i < len; i++){
27052             this.items[i].purgeListeners();
27053         }
27054         if(removeEl === true){
27055             this.el.update("");
27056             this.el.remove();
27057         }
27058     }
27059 });
27060
27061 /**
27062  * @class Roo.TabPanelItem
27063  * @extends Roo.util.Observable
27064  * Represents an individual item (tab plus body) in a TabPanel.
27065  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27066  * @param {String} id The id of this TabPanelItem
27067  * @param {String} text The text for the tab of this TabPanelItem
27068  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27069  */
27070 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27071     /**
27072      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27073      * @type Roo.TabPanel
27074      */
27075     this.tabPanel = tabPanel;
27076     /**
27077      * The id for this TabPanelItem
27078      * @type String
27079      */
27080     this.id = id;
27081     /** @private */
27082     this.disabled = false;
27083     /** @private */
27084     this.text = text;
27085     /** @private */
27086     this.loaded = false;
27087     this.closable = closable;
27088
27089     /**
27090      * The body element for this TabPanelItem.
27091      * @type Roo.Element
27092      */
27093     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27094     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27095     this.bodyEl.setStyle("display", "block");
27096     this.bodyEl.setStyle("zoom", "1");
27097     this.hideAction();
27098
27099     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27100     /** @private */
27101     this.el = Roo.get(els.el, true);
27102     this.inner = Roo.get(els.inner, true);
27103     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27104     this.pnode = Roo.get(els.el.parentNode, true);
27105     this.el.on("mousedown", this.onTabMouseDown, this);
27106     this.el.on("click", this.onTabClick, this);
27107     /** @private */
27108     if(closable){
27109         var c = Roo.get(els.close, true);
27110         c.dom.title = this.closeText;
27111         c.addClassOnOver("close-over");
27112         c.on("click", this.closeClick, this);
27113      }
27114
27115     this.addEvents({
27116          /**
27117          * @event activate
27118          * Fires when this tab becomes the active tab.
27119          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27120          * @param {Roo.TabPanelItem} this
27121          */
27122         "activate": true,
27123         /**
27124          * @event beforeclose
27125          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27126          * @param {Roo.TabPanelItem} this
27127          * @param {Object} e Set cancel to true on this object to cancel the close.
27128          */
27129         "beforeclose": true,
27130         /**
27131          * @event close
27132          * Fires when this tab is closed.
27133          * @param {Roo.TabPanelItem} this
27134          */
27135          "close": true,
27136         /**
27137          * @event deactivate
27138          * Fires when this tab is no longer the active tab.
27139          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27140          * @param {Roo.TabPanelItem} this
27141          */
27142          "deactivate" : true
27143     });
27144     this.hidden = false;
27145
27146     Roo.TabPanelItem.superclass.constructor.call(this);
27147 };
27148
27149 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27150     purgeListeners : function(){
27151        Roo.util.Observable.prototype.purgeListeners.call(this);
27152        this.el.removeAllListeners();
27153     },
27154     /**
27155      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27156      */
27157     show : function(){
27158         this.pnode.addClass("on");
27159         this.showAction();
27160         if(Roo.isOpera){
27161             this.tabPanel.stripWrap.repaint();
27162         }
27163         this.fireEvent("activate", this.tabPanel, this);
27164     },
27165
27166     /**
27167      * Returns true if this tab is the active tab.
27168      * @return {Boolean}
27169      */
27170     isActive : function(){
27171         return this.tabPanel.getActiveTab() == this;
27172     },
27173
27174     /**
27175      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27176      */
27177     hide : function(){
27178         this.pnode.removeClass("on");
27179         this.hideAction();
27180         this.fireEvent("deactivate", this.tabPanel, this);
27181     },
27182
27183     hideAction : function(){
27184         this.bodyEl.hide();
27185         this.bodyEl.setStyle("position", "absolute");
27186         this.bodyEl.setLeft("-20000px");
27187         this.bodyEl.setTop("-20000px");
27188     },
27189
27190     showAction : function(){
27191         this.bodyEl.setStyle("position", "relative");
27192         this.bodyEl.setTop("");
27193         this.bodyEl.setLeft("");
27194         this.bodyEl.show();
27195     },
27196
27197     /**
27198      * Set the tooltip for the tab.
27199      * @param {String} tooltip The tab's tooltip
27200      */
27201     setTooltip : function(text){
27202         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27203             this.textEl.dom.qtip = text;
27204             this.textEl.dom.removeAttribute('title');
27205         }else{
27206             this.textEl.dom.title = text;
27207         }
27208     },
27209
27210     onTabClick : function(e){
27211         e.preventDefault();
27212         this.tabPanel.activate(this.id);
27213     },
27214
27215     onTabMouseDown : function(e){
27216         e.preventDefault();
27217         this.tabPanel.activate(this.id);
27218     },
27219
27220     getWidth : function(){
27221         return this.inner.getWidth();
27222     },
27223
27224     setWidth : function(width){
27225         var iwidth = width - this.pnode.getPadding("lr");
27226         this.inner.setWidth(iwidth);
27227         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27228         this.pnode.setWidth(width);
27229     },
27230
27231     /**
27232      * Show or hide the tab
27233      * @param {Boolean} hidden True to hide or false to show.
27234      */
27235     setHidden : function(hidden){
27236         this.hidden = hidden;
27237         this.pnode.setStyle("display", hidden ? "none" : "");
27238     },
27239
27240     /**
27241      * Returns true if this tab is "hidden"
27242      * @return {Boolean}
27243      */
27244     isHidden : function(){
27245         return this.hidden;
27246     },
27247
27248     /**
27249      * Returns the text for this tab
27250      * @return {String}
27251      */
27252     getText : function(){
27253         return this.text;
27254     },
27255
27256     autoSize : function(){
27257         //this.el.beginMeasure();
27258         this.textEl.setWidth(1);
27259         /*
27260          *  #2804 [new] Tabs in Roojs
27261          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27262          */
27263         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27264         //this.el.endMeasure();
27265     },
27266
27267     /**
27268      * Sets the text for the tab (Note: this also sets the tooltip text)
27269      * @param {String} text The tab's text and tooltip
27270      */
27271     setText : function(text){
27272         this.text = text;
27273         this.textEl.update(text);
27274         this.setTooltip(text);
27275         if(!this.tabPanel.resizeTabs){
27276             this.autoSize();
27277         }
27278     },
27279     /**
27280      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27281      */
27282     activate : function(){
27283         this.tabPanel.activate(this.id);
27284     },
27285
27286     /**
27287      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27288      */
27289     disable : function(){
27290         if(this.tabPanel.active != this){
27291             this.disabled = true;
27292             this.pnode.addClass("disabled");
27293         }
27294     },
27295
27296     /**
27297      * Enables this TabPanelItem if it was previously disabled.
27298      */
27299     enable : function(){
27300         this.disabled = false;
27301         this.pnode.removeClass("disabled");
27302     },
27303
27304     /**
27305      * Sets the content for this TabPanelItem.
27306      * @param {String} content The content
27307      * @param {Boolean} loadScripts true to look for and load scripts
27308      */
27309     setContent : function(content, loadScripts){
27310         this.bodyEl.update(content, loadScripts);
27311     },
27312
27313     /**
27314      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27315      * @return {Roo.UpdateManager} The UpdateManager
27316      */
27317     getUpdateManager : function(){
27318         return this.bodyEl.getUpdateManager();
27319     },
27320
27321     /**
27322      * Set a URL to be used to load the content for this TabPanelItem.
27323      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27324      * @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)
27325      * @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)
27326      * @return {Roo.UpdateManager} The UpdateManager
27327      */
27328     setUrl : function(url, params, loadOnce){
27329         if(this.refreshDelegate){
27330             this.un('activate', this.refreshDelegate);
27331         }
27332         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27333         this.on("activate", this.refreshDelegate);
27334         return this.bodyEl.getUpdateManager();
27335     },
27336
27337     /** @private */
27338     _handleRefresh : function(url, params, loadOnce){
27339         if(!loadOnce || !this.loaded){
27340             var updater = this.bodyEl.getUpdateManager();
27341             updater.update(url, params, this._setLoaded.createDelegate(this));
27342         }
27343     },
27344
27345     /**
27346      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27347      *   Will fail silently if the setUrl method has not been called.
27348      *   This does not activate the panel, just updates its content.
27349      */
27350     refresh : function(){
27351         if(this.refreshDelegate){
27352            this.loaded = false;
27353            this.refreshDelegate();
27354         }
27355     },
27356
27357     /** @private */
27358     _setLoaded : function(){
27359         this.loaded = true;
27360     },
27361
27362     /** @private */
27363     closeClick : function(e){
27364         var o = {};
27365         e.stopEvent();
27366         this.fireEvent("beforeclose", this, o);
27367         if(o.cancel !== true){
27368             this.tabPanel.removeTab(this.id);
27369         }
27370     },
27371     /**
27372      * The text displayed in the tooltip for the close icon.
27373      * @type String
27374      */
27375     closeText : "Close this tab"
27376 });
27377
27378 /** @private */
27379 Roo.TabPanel.prototype.createStrip = function(container){
27380     var strip = document.createElement("div");
27381     strip.className = "x-tabs-wrap";
27382     container.appendChild(strip);
27383     return strip;
27384 };
27385 /** @private */
27386 Roo.TabPanel.prototype.createStripList = function(strip){
27387     // div wrapper for retard IE
27388     // returns the "tr" element.
27389     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27390         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27391         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27392     return strip.firstChild.firstChild.firstChild.firstChild;
27393 };
27394 /** @private */
27395 Roo.TabPanel.prototype.createBody = function(container){
27396     var body = document.createElement("div");
27397     Roo.id(body, "tab-body");
27398     Roo.fly(body).addClass("x-tabs-body");
27399     container.appendChild(body);
27400     return body;
27401 };
27402 /** @private */
27403 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27404     var body = Roo.getDom(id);
27405     if(!body){
27406         body = document.createElement("div");
27407         body.id = id;
27408     }
27409     Roo.fly(body).addClass("x-tabs-item-body");
27410     bodyEl.insertBefore(body, bodyEl.firstChild);
27411     return body;
27412 };
27413 /** @private */
27414 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27415     var td = document.createElement("td");
27416     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27417     //stripEl.appendChild(td);
27418     if(closable){
27419         td.className = "x-tabs-closable";
27420         if(!this.closeTpl){
27421             this.closeTpl = new Roo.Template(
27422                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27423                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27424                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27425             );
27426         }
27427         var el = this.closeTpl.overwrite(td, {"text": text});
27428         var close = el.getElementsByTagName("div")[0];
27429         var inner = el.getElementsByTagName("em")[0];
27430         return {"el": el, "close": close, "inner": inner};
27431     } else {
27432         if(!this.tabTpl){
27433             this.tabTpl = new Roo.Template(
27434                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27435                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27436             );
27437         }
27438         var el = this.tabTpl.overwrite(td, {"text": text});
27439         var inner = el.getElementsByTagName("em")[0];
27440         return {"el": el, "inner": inner};
27441     }
27442 };/*
27443  * Based on:
27444  * Ext JS Library 1.1.1
27445  * Copyright(c) 2006-2007, Ext JS, LLC.
27446  *
27447  * Originally Released Under LGPL - original licence link has changed is not relivant.
27448  *
27449  * Fork - LGPL
27450  * <script type="text/javascript">
27451  */
27452
27453 /**
27454  * @class Roo.Button
27455  * @extends Roo.util.Observable
27456  * Simple Button class
27457  * @cfg {String} text The button text
27458  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27459  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27460  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27461  * @cfg {Object} scope The scope of the handler
27462  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27463  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27464  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27465  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27466  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27467  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27468    applies if enableToggle = true)
27469  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27470  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27471   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27472  * @constructor
27473  * Create a new button
27474  * @param {Object} config The config object
27475  */
27476 Roo.Button = function(renderTo, config)
27477 {
27478     if (!config) {
27479         config = renderTo;
27480         renderTo = config.renderTo || false;
27481     }
27482     
27483     Roo.apply(this, config);
27484     this.addEvents({
27485         /**
27486              * @event click
27487              * Fires when this button is clicked
27488              * @param {Button} this
27489              * @param {EventObject} e The click event
27490              */
27491             "click" : true,
27492         /**
27493              * @event toggle
27494              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27495              * @param {Button} this
27496              * @param {Boolean} pressed
27497              */
27498             "toggle" : true,
27499         /**
27500              * @event mouseover
27501              * Fires when the mouse hovers over the button
27502              * @param {Button} this
27503              * @param {Event} e The event object
27504              */
27505         'mouseover' : true,
27506         /**
27507              * @event mouseout
27508              * Fires when the mouse exits the button
27509              * @param {Button} this
27510              * @param {Event} e The event object
27511              */
27512         'mouseout': true,
27513          /**
27514              * @event render
27515              * Fires when the button is rendered
27516              * @param {Button} this
27517              */
27518         'render': true
27519     });
27520     if(this.menu){
27521         this.menu = Roo.menu.MenuMgr.get(this.menu);
27522     }
27523     // register listeners first!!  - so render can be captured..
27524     Roo.util.Observable.call(this);
27525     if(renderTo){
27526         this.render(renderTo);
27527     }
27528     
27529   
27530 };
27531
27532 Roo.extend(Roo.Button, Roo.util.Observable, {
27533     /**
27534      * 
27535      */
27536     
27537     /**
27538      * Read-only. True if this button is hidden
27539      * @type Boolean
27540      */
27541     hidden : false,
27542     /**
27543      * Read-only. True if this button is disabled
27544      * @type Boolean
27545      */
27546     disabled : false,
27547     /**
27548      * Read-only. True if this button is pressed (only if enableToggle = true)
27549      * @type Boolean
27550      */
27551     pressed : false,
27552
27553     /**
27554      * @cfg {Number} tabIndex 
27555      * The DOM tabIndex for this button (defaults to undefined)
27556      */
27557     tabIndex : undefined,
27558
27559     /**
27560      * @cfg {Boolean} enableToggle
27561      * True to enable pressed/not pressed toggling (defaults to false)
27562      */
27563     enableToggle: false,
27564     /**
27565      * @cfg {Mixed} menu
27566      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27567      */
27568     menu : undefined,
27569     /**
27570      * @cfg {String} menuAlign
27571      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27572      */
27573     menuAlign : "tl-bl?",
27574
27575     /**
27576      * @cfg {String} iconCls
27577      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27578      */
27579     iconCls : undefined,
27580     /**
27581      * @cfg {String} type
27582      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27583      */
27584     type : 'button',
27585
27586     // private
27587     menuClassTarget: 'tr',
27588
27589     /**
27590      * @cfg {String} clickEvent
27591      * The type of event to map to the button's event handler (defaults to 'click')
27592      */
27593     clickEvent : 'click',
27594
27595     /**
27596      * @cfg {Boolean} handleMouseEvents
27597      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27598      */
27599     handleMouseEvents : true,
27600
27601     /**
27602      * @cfg {String} tooltipType
27603      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27604      */
27605     tooltipType : 'qtip',
27606
27607     /**
27608      * @cfg {String} cls
27609      * A CSS class to apply to the button's main element.
27610      */
27611     
27612     /**
27613      * @cfg {Roo.Template} template (Optional)
27614      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27615      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27616      * require code modifications if required elements (e.g. a button) aren't present.
27617      */
27618
27619     // private
27620     render : function(renderTo){
27621         var btn;
27622         if(this.hideParent){
27623             this.parentEl = Roo.get(renderTo);
27624         }
27625         if(!this.dhconfig){
27626             if(!this.template){
27627                 if(!Roo.Button.buttonTemplate){
27628                     // hideous table template
27629                     Roo.Button.buttonTemplate = new Roo.Template(
27630                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27631                         '<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>',
27632                         "</tr></tbody></table>");
27633                 }
27634                 this.template = Roo.Button.buttonTemplate;
27635             }
27636             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27637             var btnEl = btn.child("button:first");
27638             btnEl.on('focus', this.onFocus, this);
27639             btnEl.on('blur', this.onBlur, this);
27640             if(this.cls){
27641                 btn.addClass(this.cls);
27642             }
27643             if(this.icon){
27644                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27645             }
27646             if(this.iconCls){
27647                 btnEl.addClass(this.iconCls);
27648                 if(!this.cls){
27649                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27650                 }
27651             }
27652             if(this.tabIndex !== undefined){
27653                 btnEl.dom.tabIndex = this.tabIndex;
27654             }
27655             if(this.tooltip){
27656                 if(typeof this.tooltip == 'object'){
27657                     Roo.QuickTips.tips(Roo.apply({
27658                           target: btnEl.id
27659                     }, this.tooltip));
27660                 } else {
27661                     btnEl.dom[this.tooltipType] = this.tooltip;
27662                 }
27663             }
27664         }else{
27665             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27666         }
27667         this.el = btn;
27668         if(this.id){
27669             this.el.dom.id = this.el.id = this.id;
27670         }
27671         if(this.menu){
27672             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27673             this.menu.on("show", this.onMenuShow, this);
27674             this.menu.on("hide", this.onMenuHide, this);
27675         }
27676         btn.addClass("x-btn");
27677         if(Roo.isIE && !Roo.isIE7){
27678             this.autoWidth.defer(1, this);
27679         }else{
27680             this.autoWidth();
27681         }
27682         if(this.handleMouseEvents){
27683             btn.on("mouseover", this.onMouseOver, this);
27684             btn.on("mouseout", this.onMouseOut, this);
27685             btn.on("mousedown", this.onMouseDown, this);
27686         }
27687         btn.on(this.clickEvent, this.onClick, this);
27688         //btn.on("mouseup", this.onMouseUp, this);
27689         if(this.hidden){
27690             this.hide();
27691         }
27692         if(this.disabled){
27693             this.disable();
27694         }
27695         Roo.ButtonToggleMgr.register(this);
27696         if(this.pressed){
27697             this.el.addClass("x-btn-pressed");
27698         }
27699         if(this.repeat){
27700             var repeater = new Roo.util.ClickRepeater(btn,
27701                 typeof this.repeat == "object" ? this.repeat : {}
27702             );
27703             repeater.on("click", this.onClick,  this);
27704         }
27705         
27706         this.fireEvent('render', this);
27707         
27708     },
27709     /**
27710      * Returns the button's underlying element
27711      * @return {Roo.Element} The element
27712      */
27713     getEl : function(){
27714         return this.el;  
27715     },
27716     
27717     /**
27718      * Destroys this Button and removes any listeners.
27719      */
27720     destroy : function(){
27721         Roo.ButtonToggleMgr.unregister(this);
27722         this.el.removeAllListeners();
27723         this.purgeListeners();
27724         this.el.remove();
27725     },
27726
27727     // private
27728     autoWidth : function(){
27729         if(this.el){
27730             this.el.setWidth("auto");
27731             if(Roo.isIE7 && Roo.isStrict){
27732                 var ib = this.el.child('button');
27733                 if(ib && ib.getWidth() > 20){
27734                     ib.clip();
27735                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27736                 }
27737             }
27738             if(this.minWidth){
27739                 if(this.hidden){
27740                     this.el.beginMeasure();
27741                 }
27742                 if(this.el.getWidth() < this.minWidth){
27743                     this.el.setWidth(this.minWidth);
27744                 }
27745                 if(this.hidden){
27746                     this.el.endMeasure();
27747                 }
27748             }
27749         }
27750     },
27751
27752     /**
27753      * Assigns this button's click handler
27754      * @param {Function} handler The function to call when the button is clicked
27755      * @param {Object} scope (optional) Scope for the function passed in
27756      */
27757     setHandler : function(handler, scope){
27758         this.handler = handler;
27759         this.scope = scope;  
27760     },
27761     
27762     /**
27763      * Sets this button's text
27764      * @param {String} text The button text
27765      */
27766     setText : function(text){
27767         this.text = text;
27768         if(this.el){
27769             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27770         }
27771         this.autoWidth();
27772     },
27773     
27774     /**
27775      * Gets the text for this button
27776      * @return {String} The button text
27777      */
27778     getText : function(){
27779         return this.text;  
27780     },
27781     
27782     /**
27783      * Show this button
27784      */
27785     show: function(){
27786         this.hidden = false;
27787         if(this.el){
27788             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27789         }
27790     },
27791     
27792     /**
27793      * Hide this button
27794      */
27795     hide: function(){
27796         this.hidden = true;
27797         if(this.el){
27798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27799         }
27800     },
27801     
27802     /**
27803      * Convenience function for boolean show/hide
27804      * @param {Boolean} visible True to show, false to hide
27805      */
27806     setVisible: function(visible){
27807         if(visible) {
27808             this.show();
27809         }else{
27810             this.hide();
27811         }
27812     },
27813     
27814     /**
27815      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27816      * @param {Boolean} state (optional) Force a particular state
27817      */
27818     toggle : function(state){
27819         state = state === undefined ? !this.pressed : state;
27820         if(state != this.pressed){
27821             if(state){
27822                 this.el.addClass("x-btn-pressed");
27823                 this.pressed = true;
27824                 this.fireEvent("toggle", this, true);
27825             }else{
27826                 this.el.removeClass("x-btn-pressed");
27827                 this.pressed = false;
27828                 this.fireEvent("toggle", this, false);
27829             }
27830             if(this.toggleHandler){
27831                 this.toggleHandler.call(this.scope || this, this, state);
27832             }
27833         }
27834     },
27835     
27836     /**
27837      * Focus the button
27838      */
27839     focus : function(){
27840         this.el.child('button:first').focus();
27841     },
27842     
27843     /**
27844      * Disable this button
27845      */
27846     disable : function(){
27847         if(this.el){
27848             this.el.addClass("x-btn-disabled");
27849         }
27850         this.disabled = true;
27851     },
27852     
27853     /**
27854      * Enable this button
27855      */
27856     enable : function(){
27857         if(this.el){
27858             this.el.removeClass("x-btn-disabled");
27859         }
27860         this.disabled = false;
27861     },
27862
27863     /**
27864      * Convenience function for boolean enable/disable
27865      * @param {Boolean} enabled True to enable, false to disable
27866      */
27867     setDisabled : function(v){
27868         this[v !== true ? "enable" : "disable"]();
27869     },
27870
27871     // private
27872     onClick : function(e)
27873     {
27874         if(e){
27875             e.preventDefault();
27876         }
27877         if(e.button != 0){
27878             return;
27879         }
27880         if(!this.disabled){
27881             if(this.enableToggle){
27882                 this.toggle();
27883             }
27884             if(this.menu && !this.menu.isVisible()){
27885                 this.menu.show(this.el, this.menuAlign);
27886             }
27887             this.fireEvent("click", this, e);
27888             if(this.handler){
27889                 this.el.removeClass("x-btn-over");
27890                 this.handler.call(this.scope || this, this, e);
27891             }
27892         }
27893     },
27894     // private
27895     onMouseOver : function(e){
27896         if(!this.disabled){
27897             this.el.addClass("x-btn-over");
27898             this.fireEvent('mouseover', this, e);
27899         }
27900     },
27901     // private
27902     onMouseOut : function(e){
27903         if(!e.within(this.el,  true)){
27904             this.el.removeClass("x-btn-over");
27905             this.fireEvent('mouseout', this, e);
27906         }
27907     },
27908     // private
27909     onFocus : function(e){
27910         if(!this.disabled){
27911             this.el.addClass("x-btn-focus");
27912         }
27913     },
27914     // private
27915     onBlur : function(e){
27916         this.el.removeClass("x-btn-focus");
27917     },
27918     // private
27919     onMouseDown : function(e){
27920         if(!this.disabled && e.button == 0){
27921             this.el.addClass("x-btn-click");
27922             Roo.get(document).on('mouseup', this.onMouseUp, this);
27923         }
27924     },
27925     // private
27926     onMouseUp : function(e){
27927         if(e.button == 0){
27928             this.el.removeClass("x-btn-click");
27929             Roo.get(document).un('mouseup', this.onMouseUp, this);
27930         }
27931     },
27932     // private
27933     onMenuShow : function(e){
27934         this.el.addClass("x-btn-menu-active");
27935     },
27936     // private
27937     onMenuHide : function(e){
27938         this.el.removeClass("x-btn-menu-active");
27939     }   
27940 });
27941
27942 // Private utility class used by Button
27943 Roo.ButtonToggleMgr = function(){
27944    var groups = {};
27945    
27946    function toggleGroup(btn, state){
27947        if(state){
27948            var g = groups[btn.toggleGroup];
27949            for(var i = 0, l = g.length; i < l; i++){
27950                if(g[i] != btn){
27951                    g[i].toggle(false);
27952                }
27953            }
27954        }
27955    }
27956    
27957    return {
27958        register : function(btn){
27959            if(!btn.toggleGroup){
27960                return;
27961            }
27962            var g = groups[btn.toggleGroup];
27963            if(!g){
27964                g = groups[btn.toggleGroup] = [];
27965            }
27966            g.push(btn);
27967            btn.on("toggle", toggleGroup);
27968        },
27969        
27970        unregister : function(btn){
27971            if(!btn.toggleGroup){
27972                return;
27973            }
27974            var g = groups[btn.toggleGroup];
27975            if(g){
27976                g.remove(btn);
27977                btn.un("toggle", toggleGroup);
27978            }
27979        }
27980    };
27981 }();/*
27982  * Based on:
27983  * Ext JS Library 1.1.1
27984  * Copyright(c) 2006-2007, Ext JS, LLC.
27985  *
27986  * Originally Released Under LGPL - original licence link has changed is not relivant.
27987  *
27988  * Fork - LGPL
27989  * <script type="text/javascript">
27990  */
27991  
27992 /**
27993  * @class Roo.SplitButton
27994  * @extends Roo.Button
27995  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27996  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27997  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27998  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27999  * @cfg {String} arrowTooltip The title attribute of the arrow
28000  * @constructor
28001  * Create a new menu button
28002  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28003  * @param {Object} config The config object
28004  */
28005 Roo.SplitButton = function(renderTo, config){
28006     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28007     /**
28008      * @event arrowclick
28009      * Fires when this button's arrow is clicked
28010      * @param {SplitButton} this
28011      * @param {EventObject} e The click event
28012      */
28013     this.addEvents({"arrowclick":true});
28014 };
28015
28016 Roo.extend(Roo.SplitButton, Roo.Button, {
28017     render : function(renderTo){
28018         // this is one sweet looking template!
28019         var tpl = new Roo.Template(
28020             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28021             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28022             '<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>',
28023             "</tbody></table></td><td>",
28024             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28025             '<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>',
28026             "</tbody></table></td></tr></table>"
28027         );
28028         var btn = tpl.append(renderTo, [this.text, this.type], true);
28029         var btnEl = btn.child("button");
28030         if(this.cls){
28031             btn.addClass(this.cls);
28032         }
28033         if(this.icon){
28034             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28035         }
28036         if(this.iconCls){
28037             btnEl.addClass(this.iconCls);
28038             if(!this.cls){
28039                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28040             }
28041         }
28042         this.el = btn;
28043         if(this.handleMouseEvents){
28044             btn.on("mouseover", this.onMouseOver, this);
28045             btn.on("mouseout", this.onMouseOut, this);
28046             btn.on("mousedown", this.onMouseDown, this);
28047             btn.on("mouseup", this.onMouseUp, this);
28048         }
28049         btn.on(this.clickEvent, this.onClick, this);
28050         if(this.tooltip){
28051             if(typeof this.tooltip == 'object'){
28052                 Roo.QuickTips.tips(Roo.apply({
28053                       target: btnEl.id
28054                 }, this.tooltip));
28055             } else {
28056                 btnEl.dom[this.tooltipType] = this.tooltip;
28057             }
28058         }
28059         if(this.arrowTooltip){
28060             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28061         }
28062         if(this.hidden){
28063             this.hide();
28064         }
28065         if(this.disabled){
28066             this.disable();
28067         }
28068         if(this.pressed){
28069             this.el.addClass("x-btn-pressed");
28070         }
28071         if(Roo.isIE && !Roo.isIE7){
28072             this.autoWidth.defer(1, this);
28073         }else{
28074             this.autoWidth();
28075         }
28076         if(this.menu){
28077             this.menu.on("show", this.onMenuShow, this);
28078             this.menu.on("hide", this.onMenuHide, this);
28079         }
28080         this.fireEvent('render', this);
28081     },
28082
28083     // private
28084     autoWidth : function(){
28085         if(this.el){
28086             var tbl = this.el.child("table:first");
28087             var tbl2 = this.el.child("table:last");
28088             this.el.setWidth("auto");
28089             tbl.setWidth("auto");
28090             if(Roo.isIE7 && Roo.isStrict){
28091                 var ib = this.el.child('button:first');
28092                 if(ib && ib.getWidth() > 20){
28093                     ib.clip();
28094                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28095                 }
28096             }
28097             if(this.minWidth){
28098                 if(this.hidden){
28099                     this.el.beginMeasure();
28100                 }
28101                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28102                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28103                 }
28104                 if(this.hidden){
28105                     this.el.endMeasure();
28106                 }
28107             }
28108             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28109         } 
28110     },
28111     /**
28112      * Sets this button's click handler
28113      * @param {Function} handler The function to call when the button is clicked
28114      * @param {Object} scope (optional) Scope for the function passed above
28115      */
28116     setHandler : function(handler, scope){
28117         this.handler = handler;
28118         this.scope = scope;  
28119     },
28120     
28121     /**
28122      * Sets this button's arrow click handler
28123      * @param {Function} handler The function to call when the arrow is clicked
28124      * @param {Object} scope (optional) Scope for the function passed above
28125      */
28126     setArrowHandler : function(handler, scope){
28127         this.arrowHandler = handler;
28128         this.scope = scope;  
28129     },
28130     
28131     /**
28132      * Focus the button
28133      */
28134     focus : function(){
28135         if(this.el){
28136             this.el.child("button:first").focus();
28137         }
28138     },
28139
28140     // private
28141     onClick : function(e){
28142         e.preventDefault();
28143         if(!this.disabled){
28144             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28145                 if(this.menu && !this.menu.isVisible()){
28146                     this.menu.show(this.el, this.menuAlign);
28147                 }
28148                 this.fireEvent("arrowclick", this, e);
28149                 if(this.arrowHandler){
28150                     this.arrowHandler.call(this.scope || this, this, e);
28151                 }
28152             }else{
28153                 this.fireEvent("click", this, e);
28154                 if(this.handler){
28155                     this.handler.call(this.scope || this, this, e);
28156                 }
28157             }
28158         }
28159     },
28160     // private
28161     onMouseDown : function(e){
28162         if(!this.disabled){
28163             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28164         }
28165     },
28166     // private
28167     onMouseUp : function(e){
28168         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28169     }   
28170 });
28171
28172
28173 // backwards compat
28174 Roo.MenuButton = Roo.SplitButton;/*
28175  * Based on:
28176  * Ext JS Library 1.1.1
28177  * Copyright(c) 2006-2007, Ext JS, LLC.
28178  *
28179  * Originally Released Under LGPL - original licence link has changed is not relivant.
28180  *
28181  * Fork - LGPL
28182  * <script type="text/javascript">
28183  */
28184
28185 /**
28186  * @class Roo.Toolbar
28187  * Basic Toolbar class.
28188  * @constructor
28189  * Creates a new Toolbar
28190  * @param {Object} container The config object
28191  */ 
28192 Roo.Toolbar = function(container, buttons, config)
28193 {
28194     /// old consturctor format still supported..
28195     if(container instanceof Array){ // omit the container for later rendering
28196         buttons = container;
28197         config = buttons;
28198         container = null;
28199     }
28200     if (typeof(container) == 'object' && container.xtype) {
28201         config = container;
28202         container = config.container;
28203         buttons = config.buttons || []; // not really - use items!!
28204     }
28205     var xitems = [];
28206     if (config && config.items) {
28207         xitems = config.items;
28208         delete config.items;
28209     }
28210     Roo.apply(this, config);
28211     this.buttons = buttons;
28212     
28213     if(container){
28214         this.render(container);
28215     }
28216     this.xitems = xitems;
28217     Roo.each(xitems, function(b) {
28218         this.add(b);
28219     }, this);
28220     
28221 };
28222
28223 Roo.Toolbar.prototype = {
28224     /**
28225      * @cfg {Array} items
28226      * array of button configs or elements to add (will be converted to a MixedCollection)
28227      */
28228     
28229     /**
28230      * @cfg {String/HTMLElement/Element} container
28231      * The id or element that will contain the toolbar
28232      */
28233     // private
28234     render : function(ct){
28235         this.el = Roo.get(ct);
28236         if(this.cls){
28237             this.el.addClass(this.cls);
28238         }
28239         // using a table allows for vertical alignment
28240         // 100% width is needed by Safari...
28241         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28242         this.tr = this.el.child("tr", true);
28243         var autoId = 0;
28244         this.items = new Roo.util.MixedCollection(false, function(o){
28245             return o.id || ("item" + (++autoId));
28246         });
28247         if(this.buttons){
28248             this.add.apply(this, this.buttons);
28249             delete this.buttons;
28250         }
28251     },
28252
28253     /**
28254      * Adds element(s) to the toolbar -- this function takes a variable number of 
28255      * arguments of mixed type and adds them to the toolbar.
28256      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28257      * <ul>
28258      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28259      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28260      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28261      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28262      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28263      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28264      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28265      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28266      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28267      * </ul>
28268      * @param {Mixed} arg2
28269      * @param {Mixed} etc.
28270      */
28271     add : function(){
28272         var a = arguments, l = a.length;
28273         for(var i = 0; i < l; i++){
28274             this._add(a[i]);
28275         }
28276     },
28277     // private..
28278     _add : function(el) {
28279         
28280         if (el.xtype) {
28281             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28282         }
28283         
28284         if (el.applyTo){ // some kind of form field
28285             return this.addField(el);
28286         } 
28287         if (el.render){ // some kind of Toolbar.Item
28288             return this.addItem(el);
28289         }
28290         if (typeof el == "string"){ // string
28291             if(el == "separator" || el == "-"){
28292                 return this.addSeparator();
28293             }
28294             if (el == " "){
28295                 return this.addSpacer();
28296             }
28297             if(el == "->"){
28298                 return this.addFill();
28299             }
28300             return this.addText(el);
28301             
28302         }
28303         if(el.tagName){ // element
28304             return this.addElement(el);
28305         }
28306         if(typeof el == "object"){ // must be button config?
28307             return this.addButton(el);
28308         }
28309         // and now what?!?!
28310         return false;
28311         
28312     },
28313     
28314     /**
28315      * Add an Xtype element
28316      * @param {Object} xtype Xtype Object
28317      * @return {Object} created Object
28318      */
28319     addxtype : function(e){
28320         return this.add(e);  
28321     },
28322     
28323     /**
28324      * Returns the Element for this toolbar.
28325      * @return {Roo.Element}
28326      */
28327     getEl : function(){
28328         return this.el;  
28329     },
28330     
28331     /**
28332      * Adds a separator
28333      * @return {Roo.Toolbar.Item} The separator item
28334      */
28335     addSeparator : function(){
28336         return this.addItem(new Roo.Toolbar.Separator());
28337     },
28338
28339     /**
28340      * Adds a spacer element
28341      * @return {Roo.Toolbar.Spacer} The spacer item
28342      */
28343     addSpacer : function(){
28344         return this.addItem(new Roo.Toolbar.Spacer());
28345     },
28346
28347     /**
28348      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28349      * @return {Roo.Toolbar.Fill} The fill item
28350      */
28351     addFill : function(){
28352         return this.addItem(new Roo.Toolbar.Fill());
28353     },
28354
28355     /**
28356      * Adds any standard HTML element to the toolbar
28357      * @param {String/HTMLElement/Element} el The element or id of the element to add
28358      * @return {Roo.Toolbar.Item} The element's item
28359      */
28360     addElement : function(el){
28361         return this.addItem(new Roo.Toolbar.Item(el));
28362     },
28363     /**
28364      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28365      * @type Roo.util.MixedCollection  
28366      */
28367     items : false,
28368      
28369     /**
28370      * Adds any Toolbar.Item or subclass
28371      * @param {Roo.Toolbar.Item} item
28372      * @return {Roo.Toolbar.Item} The item
28373      */
28374     addItem : function(item){
28375         var td = this.nextBlock();
28376         item.render(td);
28377         this.items.add(item);
28378         return item;
28379     },
28380     
28381     /**
28382      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28383      * @param {Object/Array} config A button config or array of configs
28384      * @return {Roo.Toolbar.Button/Array}
28385      */
28386     addButton : function(config){
28387         if(config instanceof Array){
28388             var buttons = [];
28389             for(var i = 0, len = config.length; i < len; i++) {
28390                 buttons.push(this.addButton(config[i]));
28391             }
28392             return buttons;
28393         }
28394         var b = config;
28395         if(!(config instanceof Roo.Toolbar.Button)){
28396             b = config.split ?
28397                 new Roo.Toolbar.SplitButton(config) :
28398                 new Roo.Toolbar.Button(config);
28399         }
28400         var td = this.nextBlock();
28401         b.render(td);
28402         this.items.add(b);
28403         return b;
28404     },
28405     
28406     /**
28407      * Adds text to the toolbar
28408      * @param {String} text The text to add
28409      * @return {Roo.Toolbar.Item} The element's item
28410      */
28411     addText : function(text){
28412         return this.addItem(new Roo.Toolbar.TextItem(text));
28413     },
28414     
28415     /**
28416      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28417      * @param {Number} index The index where the item is to be inserted
28418      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28419      * @return {Roo.Toolbar.Button/Item}
28420      */
28421     insertButton : function(index, item){
28422         if(item instanceof Array){
28423             var buttons = [];
28424             for(var i = 0, len = item.length; i < len; i++) {
28425                buttons.push(this.insertButton(index + i, item[i]));
28426             }
28427             return buttons;
28428         }
28429         if (!(item instanceof Roo.Toolbar.Button)){
28430            item = new Roo.Toolbar.Button(item);
28431         }
28432         var td = document.createElement("td");
28433         this.tr.insertBefore(td, this.tr.childNodes[index]);
28434         item.render(td);
28435         this.items.insert(index, item);
28436         return item;
28437     },
28438     
28439     /**
28440      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28441      * @param {Object} config
28442      * @return {Roo.Toolbar.Item} The element's item
28443      */
28444     addDom : function(config, returnEl){
28445         var td = this.nextBlock();
28446         Roo.DomHelper.overwrite(td, config);
28447         var ti = new Roo.Toolbar.Item(td.firstChild);
28448         ti.render(td);
28449         this.items.add(ti);
28450         return ti;
28451     },
28452
28453     /**
28454      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28455      * @type Roo.util.MixedCollection  
28456      */
28457     fields : false,
28458     
28459     /**
28460      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28461      * Note: the field should not have been rendered yet. For a field that has already been
28462      * rendered, use {@link #addElement}.
28463      * @param {Roo.form.Field} field
28464      * @return {Roo.ToolbarItem}
28465      */
28466      
28467       
28468     addField : function(field) {
28469         if (!this.fields) {
28470             var autoId = 0;
28471             this.fields = new Roo.util.MixedCollection(false, function(o){
28472                 return o.id || ("item" + (++autoId));
28473             });
28474
28475         }
28476         
28477         var td = this.nextBlock();
28478         field.render(td);
28479         var ti = new Roo.Toolbar.Item(td.firstChild);
28480         ti.render(td);
28481         this.items.add(ti);
28482         this.fields.add(field);
28483         return ti;
28484     },
28485     /**
28486      * Hide the toolbar
28487      * @method hide
28488      */
28489      
28490       
28491     hide : function()
28492     {
28493         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28494         this.el.child('div').hide();
28495     },
28496     /**
28497      * Show the toolbar
28498      * @method show
28499      */
28500     show : function()
28501     {
28502         this.el.child('div').show();
28503     },
28504       
28505     // private
28506     nextBlock : function(){
28507         var td = document.createElement("td");
28508         this.tr.appendChild(td);
28509         return td;
28510     },
28511
28512     // private
28513     destroy : function(){
28514         if(this.items){ // rendered?
28515             Roo.destroy.apply(Roo, this.items.items);
28516         }
28517         if(this.fields){ // rendered?
28518             Roo.destroy.apply(Roo, this.fields.items);
28519         }
28520         Roo.Element.uncache(this.el, this.tr);
28521     }
28522 };
28523
28524 /**
28525  * @class Roo.Toolbar.Item
28526  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28527  * @constructor
28528  * Creates a new Item
28529  * @param {HTMLElement} el 
28530  */
28531 Roo.Toolbar.Item = function(el){
28532     var cfg = {};
28533     if (typeof (el.xtype) != 'undefined') {
28534         cfg = el;
28535         el = cfg.el;
28536     }
28537     
28538     this.el = Roo.getDom(el);
28539     this.id = Roo.id(this.el);
28540     this.hidden = false;
28541     
28542     this.addEvents({
28543          /**
28544              * @event render
28545              * Fires when the button is rendered
28546              * @param {Button} this
28547              */
28548         'render': true
28549     });
28550     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28551 };
28552 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28553 //Roo.Toolbar.Item.prototype = {
28554     
28555     /**
28556      * Get this item's HTML Element
28557      * @return {HTMLElement}
28558      */
28559     getEl : function(){
28560        return this.el;  
28561     },
28562
28563     // private
28564     render : function(td){
28565         
28566          this.td = td;
28567         td.appendChild(this.el);
28568         
28569         this.fireEvent('render', this);
28570     },
28571     
28572     /**
28573      * Removes and destroys this item.
28574      */
28575     destroy : function(){
28576         this.td.parentNode.removeChild(this.td);
28577     },
28578     
28579     /**
28580      * Shows this item.
28581      */
28582     show: function(){
28583         this.hidden = false;
28584         this.td.style.display = "";
28585     },
28586     
28587     /**
28588      * Hides this item.
28589      */
28590     hide: function(){
28591         this.hidden = true;
28592         this.td.style.display = "none";
28593     },
28594     
28595     /**
28596      * Convenience function for boolean show/hide.
28597      * @param {Boolean} visible true to show/false to hide
28598      */
28599     setVisible: function(visible){
28600         if(visible) {
28601             this.show();
28602         }else{
28603             this.hide();
28604         }
28605     },
28606     
28607     /**
28608      * Try to focus this item.
28609      */
28610     focus : function(){
28611         Roo.fly(this.el).focus();
28612     },
28613     
28614     /**
28615      * Disables this item.
28616      */
28617     disable : function(){
28618         Roo.fly(this.td).addClass("x-item-disabled");
28619         this.disabled = true;
28620         this.el.disabled = true;
28621     },
28622     
28623     /**
28624      * Enables this item.
28625      */
28626     enable : function(){
28627         Roo.fly(this.td).removeClass("x-item-disabled");
28628         this.disabled = false;
28629         this.el.disabled = false;
28630     }
28631 });
28632
28633
28634 /**
28635  * @class Roo.Toolbar.Separator
28636  * @extends Roo.Toolbar.Item
28637  * A simple toolbar separator class
28638  * @constructor
28639  * Creates a new Separator
28640  */
28641 Roo.Toolbar.Separator = function(cfg){
28642     
28643     var s = document.createElement("span");
28644     s.className = "ytb-sep";
28645     if (cfg) {
28646         cfg.el = s;
28647     }
28648     
28649     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28650 };
28651 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28652     enable:Roo.emptyFn,
28653     disable:Roo.emptyFn,
28654     focus:Roo.emptyFn
28655 });
28656
28657 /**
28658  * @class Roo.Toolbar.Spacer
28659  * @extends Roo.Toolbar.Item
28660  * A simple element that adds extra horizontal space to a toolbar.
28661  * @constructor
28662  * Creates a new Spacer
28663  */
28664 Roo.Toolbar.Spacer = function(cfg){
28665     var s = document.createElement("div");
28666     s.className = "ytb-spacer";
28667     if (cfg) {
28668         cfg.el = s;
28669     }
28670     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28671 };
28672 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28673     enable:Roo.emptyFn,
28674     disable:Roo.emptyFn,
28675     focus:Roo.emptyFn
28676 });
28677
28678 /**
28679  * @class Roo.Toolbar.Fill
28680  * @extends Roo.Toolbar.Spacer
28681  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28682  * @constructor
28683  * Creates a new Spacer
28684  */
28685 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28686     // private
28687     render : function(td){
28688         td.style.width = '100%';
28689         Roo.Toolbar.Fill.superclass.render.call(this, td);
28690     }
28691 });
28692
28693 /**
28694  * @class Roo.Toolbar.TextItem
28695  * @extends Roo.Toolbar.Item
28696  * A simple class that renders text directly into a toolbar.
28697  * @constructor
28698  * Creates a new TextItem
28699  * @param {String} text
28700  */
28701 Roo.Toolbar.TextItem = function(cfg){
28702     var  text = cfg || "";
28703     if (typeof(cfg) == 'object') {
28704         text = cfg.text || "";
28705     }  else {
28706         cfg = null;
28707     }
28708     var s = document.createElement("span");
28709     s.className = "ytb-text";
28710     s.innerHTML = text;
28711     if (cfg) {
28712         cfg.el  = s;
28713     }
28714     
28715     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28716 };
28717 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28718     
28719      
28720     enable:Roo.emptyFn,
28721     disable:Roo.emptyFn,
28722     focus:Roo.emptyFn
28723 });
28724
28725 /**
28726  * @class Roo.Toolbar.Button
28727  * @extends Roo.Button
28728  * A button that renders into a toolbar.
28729  * @constructor
28730  * Creates a new Button
28731  * @param {Object} config A standard {@link Roo.Button} config object
28732  */
28733 Roo.Toolbar.Button = function(config){
28734     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28735 };
28736 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28737     render : function(td){
28738         this.td = td;
28739         Roo.Toolbar.Button.superclass.render.call(this, td);
28740     },
28741     
28742     /**
28743      * Removes and destroys this button
28744      */
28745     destroy : function(){
28746         Roo.Toolbar.Button.superclass.destroy.call(this);
28747         this.td.parentNode.removeChild(this.td);
28748     },
28749     
28750     /**
28751      * Shows this button
28752      */
28753     show: function(){
28754         this.hidden = false;
28755         this.td.style.display = "";
28756     },
28757     
28758     /**
28759      * Hides this button
28760      */
28761     hide: function(){
28762         this.hidden = true;
28763         this.td.style.display = "none";
28764     },
28765
28766     /**
28767      * Disables this item
28768      */
28769     disable : function(){
28770         Roo.fly(this.td).addClass("x-item-disabled");
28771         this.disabled = true;
28772     },
28773
28774     /**
28775      * Enables this item
28776      */
28777     enable : function(){
28778         Roo.fly(this.td).removeClass("x-item-disabled");
28779         this.disabled = false;
28780     }
28781 });
28782 // backwards compat
28783 Roo.ToolbarButton = Roo.Toolbar.Button;
28784
28785 /**
28786  * @class Roo.Toolbar.SplitButton
28787  * @extends Roo.SplitButton
28788  * A menu button that renders into a toolbar.
28789  * @constructor
28790  * Creates a new SplitButton
28791  * @param {Object} config A standard {@link Roo.SplitButton} config object
28792  */
28793 Roo.Toolbar.SplitButton = function(config){
28794     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28795 };
28796 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28797     render : function(td){
28798         this.td = td;
28799         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28800     },
28801     
28802     /**
28803      * Removes and destroys this button
28804      */
28805     destroy : function(){
28806         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28807         this.td.parentNode.removeChild(this.td);
28808     },
28809     
28810     /**
28811      * Shows this button
28812      */
28813     show: function(){
28814         this.hidden = false;
28815         this.td.style.display = "";
28816     },
28817     
28818     /**
28819      * Hides this button
28820      */
28821     hide: function(){
28822         this.hidden = true;
28823         this.td.style.display = "none";
28824     }
28825 });
28826
28827 // backwards compat
28828 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28829  * Based on:
28830  * Ext JS Library 1.1.1
28831  * Copyright(c) 2006-2007, Ext JS, LLC.
28832  *
28833  * Originally Released Under LGPL - original licence link has changed is not relivant.
28834  *
28835  * Fork - LGPL
28836  * <script type="text/javascript">
28837  */
28838  
28839 /**
28840  * @class Roo.PagingToolbar
28841  * @extends Roo.Toolbar
28842  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28843  * @constructor
28844  * Create a new PagingToolbar
28845  * @param {Object} config The config object
28846  */
28847 Roo.PagingToolbar = function(el, ds, config)
28848 {
28849     // old args format still supported... - xtype is prefered..
28850     if (typeof(el) == 'object' && el.xtype) {
28851         // created from xtype...
28852         config = el;
28853         ds = el.dataSource;
28854         el = config.container;
28855     }
28856     var items = [];
28857     if (config.items) {
28858         items = config.items;
28859         config.items = [];
28860     }
28861     
28862     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28863     this.ds = ds;
28864     this.cursor = 0;
28865     this.renderButtons(this.el);
28866     this.bind(ds);
28867     
28868     // supprot items array.
28869    
28870     Roo.each(items, function(e) {
28871         this.add(Roo.factory(e));
28872     },this);
28873     
28874 };
28875
28876 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28877     /**
28878      * @cfg {Roo.data.Store} dataSource
28879      * The underlying data store providing the paged data
28880      */
28881     /**
28882      * @cfg {String/HTMLElement/Element} container
28883      * container The id or element that will contain the toolbar
28884      */
28885     /**
28886      * @cfg {Boolean} displayInfo
28887      * True to display the displayMsg (defaults to false)
28888      */
28889     /**
28890      * @cfg {Number} pageSize
28891      * The number of records to display per page (defaults to 20)
28892      */
28893     pageSize: 20,
28894     /**
28895      * @cfg {String} displayMsg
28896      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28897      */
28898     displayMsg : 'Displaying {0} - {1} of {2}',
28899     /**
28900      * @cfg {String} emptyMsg
28901      * The message to display when no records are found (defaults to "No data to display")
28902      */
28903     emptyMsg : 'No data to display',
28904     /**
28905      * Customizable piece of the default paging text (defaults to "Page")
28906      * @type String
28907      */
28908     beforePageText : "Page",
28909     /**
28910      * Customizable piece of the default paging text (defaults to "of %0")
28911      * @type String
28912      */
28913     afterPageText : "of {0}",
28914     /**
28915      * Customizable piece of the default paging text (defaults to "First Page")
28916      * @type String
28917      */
28918     firstText : "First Page",
28919     /**
28920      * Customizable piece of the default paging text (defaults to "Previous Page")
28921      * @type String
28922      */
28923     prevText : "Previous Page",
28924     /**
28925      * Customizable piece of the default paging text (defaults to "Next Page")
28926      * @type String
28927      */
28928     nextText : "Next Page",
28929     /**
28930      * Customizable piece of the default paging text (defaults to "Last Page")
28931      * @type String
28932      */
28933     lastText : "Last Page",
28934     /**
28935      * Customizable piece of the default paging text (defaults to "Refresh")
28936      * @type String
28937      */
28938     refreshText : "Refresh",
28939
28940     // private
28941     renderButtons : function(el){
28942         Roo.PagingToolbar.superclass.render.call(this, el);
28943         this.first = this.addButton({
28944             tooltip: this.firstText,
28945             cls: "x-btn-icon x-grid-page-first",
28946             disabled: true,
28947             handler: this.onClick.createDelegate(this, ["first"])
28948         });
28949         this.prev = this.addButton({
28950             tooltip: this.prevText,
28951             cls: "x-btn-icon x-grid-page-prev",
28952             disabled: true,
28953             handler: this.onClick.createDelegate(this, ["prev"])
28954         });
28955         //this.addSeparator();
28956         this.add(this.beforePageText);
28957         this.field = Roo.get(this.addDom({
28958            tag: "input",
28959            type: "text",
28960            size: "3",
28961            value: "1",
28962            cls: "x-grid-page-number"
28963         }).el);
28964         this.field.on("keydown", this.onPagingKeydown, this);
28965         this.field.on("focus", function(){this.dom.select();});
28966         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28967         this.field.setHeight(18);
28968         //this.addSeparator();
28969         this.next = this.addButton({
28970             tooltip: this.nextText,
28971             cls: "x-btn-icon x-grid-page-next",
28972             disabled: true,
28973             handler: this.onClick.createDelegate(this, ["next"])
28974         });
28975         this.last = this.addButton({
28976             tooltip: this.lastText,
28977             cls: "x-btn-icon x-grid-page-last",
28978             disabled: true,
28979             handler: this.onClick.createDelegate(this, ["last"])
28980         });
28981         //this.addSeparator();
28982         this.loading = this.addButton({
28983             tooltip: this.refreshText,
28984             cls: "x-btn-icon x-grid-loading",
28985             handler: this.onClick.createDelegate(this, ["refresh"])
28986         });
28987
28988         if(this.displayInfo){
28989             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28990         }
28991     },
28992
28993     // private
28994     updateInfo : function(){
28995         if(this.displayEl){
28996             var count = this.ds.getCount();
28997             var msg = count == 0 ?
28998                 this.emptyMsg :
28999                 String.format(
29000                     this.displayMsg,
29001                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29002                 );
29003             this.displayEl.update(msg);
29004         }
29005     },
29006
29007     // private
29008     onLoad : function(ds, r, o){
29009        this.cursor = o.params ? o.params.start : 0;
29010        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29011
29012        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29013        this.field.dom.value = ap;
29014        this.first.setDisabled(ap == 1);
29015        this.prev.setDisabled(ap == 1);
29016        this.next.setDisabled(ap == ps);
29017        this.last.setDisabled(ap == ps);
29018        this.loading.enable();
29019        this.updateInfo();
29020     },
29021
29022     // private
29023     getPageData : function(){
29024         var total = this.ds.getTotalCount();
29025         return {
29026             total : total,
29027             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29028             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29029         };
29030     },
29031
29032     // private
29033     onLoadError : function(){
29034         this.loading.enable();
29035     },
29036
29037     // private
29038     onPagingKeydown : function(e){
29039         var k = e.getKey();
29040         var d = this.getPageData();
29041         if(k == e.RETURN){
29042             var v = this.field.dom.value, pageNum;
29043             if(!v || isNaN(pageNum = parseInt(v, 10))){
29044                 this.field.dom.value = d.activePage;
29045                 return;
29046             }
29047             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29048             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29049             e.stopEvent();
29050         }
29051         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))
29052         {
29053           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29054           this.field.dom.value = pageNum;
29055           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29056           e.stopEvent();
29057         }
29058         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29059         {
29060           var v = this.field.dom.value, pageNum; 
29061           var increment = (e.shiftKey) ? 10 : 1;
29062           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29063             increment *= -1;
29064           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29065             this.field.dom.value = d.activePage;
29066             return;
29067           }
29068           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29069           {
29070             this.field.dom.value = parseInt(v, 10) + increment;
29071             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29072             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29073           }
29074           e.stopEvent();
29075         }
29076     },
29077
29078     // private
29079     beforeLoad : function(){
29080         if(this.loading){
29081             this.loading.disable();
29082         }
29083     },
29084
29085     // private
29086     onClick : function(which){
29087         var ds = this.ds;
29088         switch(which){
29089             case "first":
29090                 ds.load({params:{start: 0, limit: this.pageSize}});
29091             break;
29092             case "prev":
29093                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29094             break;
29095             case "next":
29096                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29097             break;
29098             case "last":
29099                 var total = ds.getTotalCount();
29100                 var extra = total % this.pageSize;
29101                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29102                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29103             break;
29104             case "refresh":
29105                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29106             break;
29107         }
29108     },
29109
29110     /**
29111      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29112      * @param {Roo.data.Store} store The data store to unbind
29113      */
29114     unbind : function(ds){
29115         ds.un("beforeload", this.beforeLoad, this);
29116         ds.un("load", this.onLoad, this);
29117         ds.un("loadexception", this.onLoadError, this);
29118         ds.un("remove", this.updateInfo, this);
29119         ds.un("add", this.updateInfo, this);
29120         this.ds = undefined;
29121     },
29122
29123     /**
29124      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29125      * @param {Roo.data.Store} store The data store to bind
29126      */
29127     bind : function(ds){
29128         ds.on("beforeload", this.beforeLoad, this);
29129         ds.on("load", this.onLoad, this);
29130         ds.on("loadexception", this.onLoadError, this);
29131         ds.on("remove", this.updateInfo, this);
29132         ds.on("add", this.updateInfo, this);
29133         this.ds = ds;
29134     }
29135 });/*
29136  * Based on:
29137  * Ext JS Library 1.1.1
29138  * Copyright(c) 2006-2007, Ext JS, LLC.
29139  *
29140  * Originally Released Under LGPL - original licence link has changed is not relivant.
29141  *
29142  * Fork - LGPL
29143  * <script type="text/javascript">
29144  */
29145
29146 /**
29147  * @class Roo.Resizable
29148  * @extends Roo.util.Observable
29149  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29150  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29151  * 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
29152  * the element will be wrapped for you automatically.</p>
29153  * <p>Here is the list of valid resize handles:</p>
29154  * <pre>
29155 Value   Description
29156 ------  -------------------
29157  'n'     north
29158  's'     south
29159  'e'     east
29160  'w'     west
29161  'nw'    northwest
29162  'sw'    southwest
29163  'se'    southeast
29164  'ne'    northeast
29165  'hd'    horizontal drag
29166  'all'   all
29167 </pre>
29168  * <p>Here's an example showing the creation of a typical Resizable:</p>
29169  * <pre><code>
29170 var resizer = new Roo.Resizable("element-id", {
29171     handles: 'all',
29172     minWidth: 200,
29173     minHeight: 100,
29174     maxWidth: 500,
29175     maxHeight: 400,
29176     pinned: true
29177 });
29178 resizer.on("resize", myHandler);
29179 </code></pre>
29180  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29181  * resizer.east.setDisplayed(false);</p>
29182  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29183  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29184  * resize operation's new size (defaults to [0, 0])
29185  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29186  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29187  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29188  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29189  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29190  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29191  * @cfg {Number} width The width of the element in pixels (defaults to null)
29192  * @cfg {Number} height The height of the element in pixels (defaults to null)
29193  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29194  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29195  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29196  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29197  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29198  * in favor of the handles config option (defaults to false)
29199  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29200  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29201  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29202  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29203  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29204  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29205  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29206  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29207  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29208  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29209  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29210  * @constructor
29211  * Create a new resizable component
29212  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29213  * @param {Object} config configuration options
29214   */
29215 Roo.Resizable = function(el, config)
29216 {
29217     this.el = Roo.get(el);
29218
29219     if(config && config.wrap){
29220         config.resizeChild = this.el;
29221         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29222         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29223         this.el.setStyle("overflow", "hidden");
29224         this.el.setPositioning(config.resizeChild.getPositioning());
29225         config.resizeChild.clearPositioning();
29226         if(!config.width || !config.height){
29227             var csize = config.resizeChild.getSize();
29228             this.el.setSize(csize.width, csize.height);
29229         }
29230         if(config.pinned && !config.adjustments){
29231             config.adjustments = "auto";
29232         }
29233     }
29234
29235     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29236     this.proxy.unselectable();
29237     this.proxy.enableDisplayMode('block');
29238
29239     Roo.apply(this, config);
29240
29241     if(this.pinned){
29242         this.disableTrackOver = true;
29243         this.el.addClass("x-resizable-pinned");
29244     }
29245     // if the element isn't positioned, make it relative
29246     var position = this.el.getStyle("position");
29247     if(position != "absolute" && position != "fixed"){
29248         this.el.setStyle("position", "relative");
29249     }
29250     if(!this.handles){ // no handles passed, must be legacy style
29251         this.handles = 's,e,se';
29252         if(this.multiDirectional){
29253             this.handles += ',n,w';
29254         }
29255     }
29256     if(this.handles == "all"){
29257         this.handles = "n s e w ne nw se sw";
29258     }
29259     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29260     var ps = Roo.Resizable.positions;
29261     for(var i = 0, len = hs.length; i < len; i++){
29262         if(hs[i] && ps[hs[i]]){
29263             var pos = ps[hs[i]];
29264             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29265         }
29266     }
29267     // legacy
29268     this.corner = this.southeast;
29269     
29270     // updateBox = the box can move..
29271     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29272         this.updateBox = true;
29273     }
29274
29275     this.activeHandle = null;
29276
29277     if(this.resizeChild){
29278         if(typeof this.resizeChild == "boolean"){
29279             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29280         }else{
29281             this.resizeChild = Roo.get(this.resizeChild, true);
29282         }
29283     }
29284     
29285     if(this.adjustments == "auto"){
29286         var rc = this.resizeChild;
29287         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29288         if(rc && (hw || hn)){
29289             rc.position("relative");
29290             rc.setLeft(hw ? hw.el.getWidth() : 0);
29291             rc.setTop(hn ? hn.el.getHeight() : 0);
29292         }
29293         this.adjustments = [
29294             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29295             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29296         ];
29297     }
29298
29299     if(this.draggable){
29300         this.dd = this.dynamic ?
29301             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29302         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29303     }
29304
29305     // public events
29306     this.addEvents({
29307         /**
29308          * @event beforeresize
29309          * Fired before resize is allowed. Set enabled to false to cancel resize.
29310          * @param {Roo.Resizable} this
29311          * @param {Roo.EventObject} e The mousedown event
29312          */
29313         "beforeresize" : true,
29314         /**
29315          * @event resizing
29316          * Fired a resizing.
29317          * @param {Roo.Resizable} this
29318          * @param {Number} x The new x position
29319          * @param {Number} y The new y position
29320          * @param {Number} w The new w width
29321          * @param {Number} h The new h hight
29322          * @param {Roo.EventObject} e The mouseup event
29323          */
29324         "resizing" : true,
29325         /**
29326          * @event resize
29327          * Fired after a resize.
29328          * @param {Roo.Resizable} this
29329          * @param {Number} width The new width
29330          * @param {Number} height The new height
29331          * @param {Roo.EventObject} e The mouseup event
29332          */
29333         "resize" : true
29334     });
29335
29336     if(this.width !== null && this.height !== null){
29337         this.resizeTo(this.width, this.height);
29338     }else{
29339         this.updateChildSize();
29340     }
29341     if(Roo.isIE){
29342         this.el.dom.style.zoom = 1;
29343     }
29344     Roo.Resizable.superclass.constructor.call(this);
29345 };
29346
29347 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29348         resizeChild : false,
29349         adjustments : [0, 0],
29350         minWidth : 5,
29351         minHeight : 5,
29352         maxWidth : 10000,
29353         maxHeight : 10000,
29354         enabled : true,
29355         animate : false,
29356         duration : .35,
29357         dynamic : false,
29358         handles : false,
29359         multiDirectional : false,
29360         disableTrackOver : false,
29361         easing : 'easeOutStrong',
29362         widthIncrement : 0,
29363         heightIncrement : 0,
29364         pinned : false,
29365         width : null,
29366         height : null,
29367         preserveRatio : false,
29368         transparent: false,
29369         minX: 0,
29370         minY: 0,
29371         draggable: false,
29372
29373         /**
29374          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29375          */
29376         constrainTo: undefined,
29377         /**
29378          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29379          */
29380         resizeRegion: undefined,
29381
29382
29383     /**
29384      * Perform a manual resize
29385      * @param {Number} width
29386      * @param {Number} height
29387      */
29388     resizeTo : function(width, height){
29389         this.el.setSize(width, height);
29390         this.updateChildSize();
29391         this.fireEvent("resize", this, width, height, null);
29392     },
29393
29394     // private
29395     startSizing : function(e, handle){
29396         this.fireEvent("beforeresize", this, e);
29397         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29398
29399             if(!this.overlay){
29400                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29401                 this.overlay.unselectable();
29402                 this.overlay.enableDisplayMode("block");
29403                 this.overlay.on("mousemove", this.onMouseMove, this);
29404                 this.overlay.on("mouseup", this.onMouseUp, this);
29405             }
29406             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29407
29408             this.resizing = true;
29409             this.startBox = this.el.getBox();
29410             this.startPoint = e.getXY();
29411             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29412                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29413
29414             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29415             this.overlay.show();
29416
29417             if(this.constrainTo) {
29418                 var ct = Roo.get(this.constrainTo);
29419                 this.resizeRegion = ct.getRegion().adjust(
29420                     ct.getFrameWidth('t'),
29421                     ct.getFrameWidth('l'),
29422                     -ct.getFrameWidth('b'),
29423                     -ct.getFrameWidth('r')
29424                 );
29425             }
29426
29427             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29428             this.proxy.show();
29429             this.proxy.setBox(this.startBox);
29430             if(!this.dynamic){
29431                 this.proxy.setStyle('visibility', 'visible');
29432             }
29433         }
29434     },
29435
29436     // private
29437     onMouseDown : function(handle, e){
29438         if(this.enabled){
29439             e.stopEvent();
29440             this.activeHandle = handle;
29441             this.startSizing(e, handle);
29442         }
29443     },
29444
29445     // private
29446     onMouseUp : function(e){
29447         var size = this.resizeElement();
29448         this.resizing = false;
29449         this.handleOut();
29450         this.overlay.hide();
29451         this.proxy.hide();
29452         this.fireEvent("resize", this, size.width, size.height, e);
29453     },
29454
29455     // private
29456     updateChildSize : function(){
29457         
29458         if(this.resizeChild){
29459             var el = this.el;
29460             var child = this.resizeChild;
29461             var adj = this.adjustments;
29462             if(el.dom.offsetWidth){
29463                 var b = el.getSize(true);
29464                 child.setSize(b.width+adj[0], b.height+adj[1]);
29465             }
29466             // Second call here for IE
29467             // The first call enables instant resizing and
29468             // the second call corrects scroll bars if they
29469             // exist
29470             if(Roo.isIE){
29471                 setTimeout(function(){
29472                     if(el.dom.offsetWidth){
29473                         var b = el.getSize(true);
29474                         child.setSize(b.width+adj[0], b.height+adj[1]);
29475                     }
29476                 }, 10);
29477             }
29478         }
29479     },
29480
29481     // private
29482     snap : function(value, inc, min){
29483         if(!inc || !value) return value;
29484         var newValue = value;
29485         var m = value % inc;
29486         if(m > 0){
29487             if(m > (inc/2)){
29488                 newValue = value + (inc-m);
29489             }else{
29490                 newValue = value - m;
29491             }
29492         }
29493         return Math.max(min, newValue);
29494     },
29495
29496     // private
29497     resizeElement : function(){
29498         var box = this.proxy.getBox();
29499         if(this.updateBox){
29500             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29501         }else{
29502             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29503         }
29504         this.updateChildSize();
29505         if(!this.dynamic){
29506             this.proxy.hide();
29507         }
29508         return box;
29509     },
29510
29511     // private
29512     constrain : function(v, diff, m, mx){
29513         if(v - diff < m){
29514             diff = v - m;
29515         }else if(v - diff > mx){
29516             diff = mx - v;
29517         }
29518         return diff;
29519     },
29520
29521     // private
29522     onMouseMove : function(e){
29523         
29524         if(this.enabled){
29525             try{// try catch so if something goes wrong the user doesn't get hung
29526
29527             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29528                 return;
29529             }
29530
29531             //var curXY = this.startPoint;
29532             var curSize = this.curSize || this.startBox;
29533             var x = this.startBox.x, y = this.startBox.y;
29534             var ox = x, oy = y;
29535             var w = curSize.width, h = curSize.height;
29536             var ow = w, oh = h;
29537             var mw = this.minWidth, mh = this.minHeight;
29538             var mxw = this.maxWidth, mxh = this.maxHeight;
29539             var wi = this.widthIncrement;
29540             var hi = this.heightIncrement;
29541
29542             var eventXY = e.getXY();
29543             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29544             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29545
29546             var pos = this.activeHandle.position;
29547
29548             switch(pos){
29549                 case "east":
29550                     w += diffX;
29551                     w = Math.min(Math.max(mw, w), mxw);
29552                     break;
29553              
29554                 case "south":
29555                     h += diffY;
29556                     h = Math.min(Math.max(mh, h), mxh);
29557                     break;
29558                 case "southeast":
29559                     w += diffX;
29560                     h += diffY;
29561                     w = Math.min(Math.max(mw, w), mxw);
29562                     h = Math.min(Math.max(mh, h), mxh);
29563                     break;
29564                 case "north":
29565                     diffY = this.constrain(h, diffY, mh, mxh);
29566                     y += diffY;
29567                     h -= diffY;
29568                     break;
29569                 case "hdrag":
29570                     
29571                     if (wi) {
29572                         var adiffX = Math.abs(diffX);
29573                         var sub = (adiffX % wi); // how much 
29574                         if (sub > (wi/2)) { // far enough to snap
29575                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29576                         } else {
29577                             // remove difference.. 
29578                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29579                         }
29580                     }
29581                     x += diffX;
29582                     x = Math.max(this.minX, x);
29583                     break;
29584                 case "west":
29585                     diffX = this.constrain(w, diffX, mw, mxw);
29586                     x += diffX;
29587                     w -= diffX;
29588                     break;
29589                 case "northeast":
29590                     w += diffX;
29591                     w = Math.min(Math.max(mw, w), mxw);
29592                     diffY = this.constrain(h, diffY, mh, mxh);
29593                     y += diffY;
29594                     h -= diffY;
29595                     break;
29596                 case "northwest":
29597                     diffX = this.constrain(w, diffX, mw, mxw);
29598                     diffY = this.constrain(h, diffY, mh, mxh);
29599                     y += diffY;
29600                     h -= diffY;
29601                     x += diffX;
29602                     w -= diffX;
29603                     break;
29604                case "southwest":
29605                     diffX = this.constrain(w, diffX, mw, mxw);
29606                     h += diffY;
29607                     h = Math.min(Math.max(mh, h), mxh);
29608                     x += diffX;
29609                     w -= diffX;
29610                     break;
29611             }
29612
29613             var sw = this.snap(w, wi, mw);
29614             var sh = this.snap(h, hi, mh);
29615             if(sw != w || sh != h){
29616                 switch(pos){
29617                     case "northeast":
29618                         y -= sh - h;
29619                     break;
29620                     case "north":
29621                         y -= sh - h;
29622                         break;
29623                     case "southwest":
29624                         x -= sw - w;
29625                     break;
29626                     case "west":
29627                         x -= sw - w;
29628                         break;
29629                     case "northwest":
29630                         x -= sw - w;
29631                         y -= sh - h;
29632                     break;
29633                 }
29634                 w = sw;
29635                 h = sh;
29636             }
29637
29638             if(this.preserveRatio){
29639                 switch(pos){
29640                     case "southeast":
29641                     case "east":
29642                         h = oh * (w/ow);
29643                         h = Math.min(Math.max(mh, h), mxh);
29644                         w = ow * (h/oh);
29645                        break;
29646                     case "south":
29647                         w = ow * (h/oh);
29648                         w = Math.min(Math.max(mw, w), mxw);
29649                         h = oh * (w/ow);
29650                         break;
29651                     case "northeast":
29652                         w = ow * (h/oh);
29653                         w = Math.min(Math.max(mw, w), mxw);
29654                         h = oh * (w/ow);
29655                     break;
29656                     case "north":
29657                         var tw = w;
29658                         w = ow * (h/oh);
29659                         w = Math.min(Math.max(mw, w), mxw);
29660                         h = oh * (w/ow);
29661                         x += (tw - w) / 2;
29662                         break;
29663                     case "southwest":
29664                         h = oh * (w/ow);
29665                         h = Math.min(Math.max(mh, h), mxh);
29666                         var tw = w;
29667                         w = ow * (h/oh);
29668                         x += tw - w;
29669                         break;
29670                     case "west":
29671                         var th = h;
29672                         h = oh * (w/ow);
29673                         h = Math.min(Math.max(mh, h), mxh);
29674                         y += (th - h) / 2;
29675                         var tw = w;
29676                         w = ow * (h/oh);
29677                         x += tw - w;
29678                        break;
29679                     case "northwest":
29680                         var tw = w;
29681                         var th = h;
29682                         h = oh * (w/ow);
29683                         h = Math.min(Math.max(mh, h), mxh);
29684                         w = ow * (h/oh);
29685                         y += th - h;
29686                         x += tw - w;
29687                        break;
29688
29689                 }
29690             }
29691             if (pos == 'hdrag') {
29692                 w = ow;
29693             }
29694             this.proxy.setBounds(x, y, w, h);
29695             if(this.dynamic){
29696                 this.resizeElement();
29697             }
29698             }catch(e){}
29699         }
29700         this.fireEvent("resizing", this, x, y, w, h, e);
29701     },
29702
29703     // private
29704     handleOver : function(){
29705         if(this.enabled){
29706             this.el.addClass("x-resizable-over");
29707         }
29708     },
29709
29710     // private
29711     handleOut : function(){
29712         if(!this.resizing){
29713             this.el.removeClass("x-resizable-over");
29714         }
29715     },
29716
29717     /**
29718      * Returns the element this component is bound to.
29719      * @return {Roo.Element}
29720      */
29721     getEl : function(){
29722         return this.el;
29723     },
29724
29725     /**
29726      * Returns the resizeChild element (or null).
29727      * @return {Roo.Element}
29728      */
29729     getResizeChild : function(){
29730         return this.resizeChild;
29731     },
29732     groupHandler : function()
29733     {
29734         
29735     },
29736     /**
29737      * Destroys this resizable. If the element was wrapped and
29738      * removeEl is not true then the element remains.
29739      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29740      */
29741     destroy : function(removeEl){
29742         this.proxy.remove();
29743         if(this.overlay){
29744             this.overlay.removeAllListeners();
29745             this.overlay.remove();
29746         }
29747         var ps = Roo.Resizable.positions;
29748         for(var k in ps){
29749             if(typeof ps[k] != "function" && this[ps[k]]){
29750                 var h = this[ps[k]];
29751                 h.el.removeAllListeners();
29752                 h.el.remove();
29753             }
29754         }
29755         if(removeEl){
29756             this.el.update("");
29757             this.el.remove();
29758         }
29759     }
29760 });
29761
29762 // private
29763 // hash to map config positions to true positions
29764 Roo.Resizable.positions = {
29765     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29766     hd: "hdrag"
29767 };
29768
29769 // private
29770 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29771     if(!this.tpl){
29772         // only initialize the template if resizable is used
29773         var tpl = Roo.DomHelper.createTemplate(
29774             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29775         );
29776         tpl.compile();
29777         Roo.Resizable.Handle.prototype.tpl = tpl;
29778     }
29779     this.position = pos;
29780     this.rz = rz;
29781     // show north drag fro topdra
29782     var handlepos = pos == 'hdrag' ? 'north' : pos;
29783     
29784     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29785     if (pos == 'hdrag') {
29786         this.el.setStyle('cursor', 'pointer');
29787     }
29788     this.el.unselectable();
29789     if(transparent){
29790         this.el.setOpacity(0);
29791     }
29792     this.el.on("mousedown", this.onMouseDown, this);
29793     if(!disableTrackOver){
29794         this.el.on("mouseover", this.onMouseOver, this);
29795         this.el.on("mouseout", this.onMouseOut, this);
29796     }
29797 };
29798
29799 // private
29800 Roo.Resizable.Handle.prototype = {
29801     afterResize : function(rz){
29802         Roo.log('after?');
29803         // do nothing
29804     },
29805     // private
29806     onMouseDown : function(e){
29807         this.rz.onMouseDown(this, e);
29808     },
29809     // private
29810     onMouseOver : function(e){
29811         this.rz.handleOver(this, e);
29812     },
29813     // private
29814     onMouseOut : function(e){
29815         this.rz.handleOut(this, e);
29816     }
29817 };/*
29818  * Based on:
29819  * Ext JS Library 1.1.1
29820  * Copyright(c) 2006-2007, Ext JS, LLC.
29821  *
29822  * Originally Released Under LGPL - original licence link has changed is not relivant.
29823  *
29824  * Fork - LGPL
29825  * <script type="text/javascript">
29826  */
29827
29828 /**
29829  * @class Roo.Editor
29830  * @extends Roo.Component
29831  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29832  * @constructor
29833  * Create a new Editor
29834  * @param {Roo.form.Field} field The Field object (or descendant)
29835  * @param {Object} config The config object
29836  */
29837 Roo.Editor = function(field, config){
29838     Roo.Editor.superclass.constructor.call(this, config);
29839     this.field = field;
29840     this.addEvents({
29841         /**
29842              * @event beforestartedit
29843              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29844              * false from the handler of this event.
29845              * @param {Editor} this
29846              * @param {Roo.Element} boundEl The underlying element bound to this editor
29847              * @param {Mixed} value The field value being set
29848              */
29849         "beforestartedit" : true,
29850         /**
29851              * @event startedit
29852              * Fires when this editor is displayed
29853              * @param {Roo.Element} boundEl The underlying element bound to this editor
29854              * @param {Mixed} value The starting field value
29855              */
29856         "startedit" : true,
29857         /**
29858              * @event beforecomplete
29859              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29860              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29861              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29862              * event will not fire since no edit actually occurred.
29863              * @param {Editor} this
29864              * @param {Mixed} value The current field value
29865              * @param {Mixed} startValue The original field value
29866              */
29867         "beforecomplete" : true,
29868         /**
29869              * @event complete
29870              * Fires after editing is complete and any changed value has been written to the underlying field.
29871              * @param {Editor} this
29872              * @param {Mixed} value The current field value
29873              * @param {Mixed} startValue The original field value
29874              */
29875         "complete" : true,
29876         /**
29877          * @event specialkey
29878          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29879          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29880          * @param {Roo.form.Field} this
29881          * @param {Roo.EventObject} e The event object
29882          */
29883         "specialkey" : true
29884     });
29885 };
29886
29887 Roo.extend(Roo.Editor, Roo.Component, {
29888     /**
29889      * @cfg {Boolean/String} autosize
29890      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29891      * or "height" to adopt the height only (defaults to false)
29892      */
29893     /**
29894      * @cfg {Boolean} revertInvalid
29895      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29896      * validation fails (defaults to true)
29897      */
29898     /**
29899      * @cfg {Boolean} ignoreNoChange
29900      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29901      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29902      * will never be ignored.
29903      */
29904     /**
29905      * @cfg {Boolean} hideEl
29906      * False to keep the bound element visible while the editor is displayed (defaults to true)
29907      */
29908     /**
29909      * @cfg {Mixed} value
29910      * The data value of the underlying field (defaults to "")
29911      */
29912     value : "",
29913     /**
29914      * @cfg {String} alignment
29915      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29916      */
29917     alignment: "c-c?",
29918     /**
29919      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29920      * for bottom-right shadow (defaults to "frame")
29921      */
29922     shadow : "frame",
29923     /**
29924      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29925      */
29926     constrain : false,
29927     /**
29928      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29929      */
29930     completeOnEnter : false,
29931     /**
29932      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29933      */
29934     cancelOnEsc : false,
29935     /**
29936      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29937      */
29938     updateEl : false,
29939
29940     // private
29941     onRender : function(ct, position){
29942         this.el = new Roo.Layer({
29943             shadow: this.shadow,
29944             cls: "x-editor",
29945             parentEl : ct,
29946             shim : this.shim,
29947             shadowOffset:4,
29948             id: this.id,
29949             constrain: this.constrain
29950         });
29951         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29952         if(this.field.msgTarget != 'title'){
29953             this.field.msgTarget = 'qtip';
29954         }
29955         this.field.render(this.el);
29956         if(Roo.isGecko){
29957             this.field.el.dom.setAttribute('autocomplete', 'off');
29958         }
29959         this.field.on("specialkey", this.onSpecialKey, this);
29960         if(this.swallowKeys){
29961             this.field.el.swallowEvent(['keydown','keypress']);
29962         }
29963         this.field.show();
29964         this.field.on("blur", this.onBlur, this);
29965         if(this.field.grow){
29966             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29967         }
29968     },
29969
29970     onSpecialKey : function(field, e)
29971     {
29972         //Roo.log('editor onSpecialKey');
29973         if(this.completeOnEnter && e.getKey() == e.ENTER){
29974             e.stopEvent();
29975             this.completeEdit();
29976             return;
29977         }
29978         // do not fire special key otherwise it might hide close the editor...
29979         if(e.getKey() == e.ENTER){    
29980             return;
29981         }
29982         if(this.cancelOnEsc && e.getKey() == e.ESC){
29983             this.cancelEdit();
29984             return;
29985         } 
29986         this.fireEvent('specialkey', field, e);
29987     
29988     },
29989
29990     /**
29991      * Starts the editing process and shows the editor.
29992      * @param {String/HTMLElement/Element} el The element to edit
29993      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29994       * to the innerHTML of el.
29995      */
29996     startEdit : function(el, value){
29997         if(this.editing){
29998             this.completeEdit();
29999         }
30000         this.boundEl = Roo.get(el);
30001         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30002         if(!this.rendered){
30003             this.render(this.parentEl || document.body);
30004         }
30005         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30006             return;
30007         }
30008         this.startValue = v;
30009         this.field.setValue(v);
30010         if(this.autoSize){
30011             var sz = this.boundEl.getSize();
30012             switch(this.autoSize){
30013                 case "width":
30014                 this.setSize(sz.width,  "");
30015                 break;
30016                 case "height":
30017                 this.setSize("",  sz.height);
30018                 break;
30019                 default:
30020                 this.setSize(sz.width,  sz.height);
30021             }
30022         }
30023         this.el.alignTo(this.boundEl, this.alignment);
30024         this.editing = true;
30025         if(Roo.QuickTips){
30026             Roo.QuickTips.disable();
30027         }
30028         this.show();
30029     },
30030
30031     /**
30032      * Sets the height and width of this editor.
30033      * @param {Number} width The new width
30034      * @param {Number} height The new height
30035      */
30036     setSize : function(w, h){
30037         this.field.setSize(w, h);
30038         if(this.el){
30039             this.el.sync();
30040         }
30041     },
30042
30043     /**
30044      * Realigns the editor to the bound field based on the current alignment config value.
30045      */
30046     realign : function(){
30047         this.el.alignTo(this.boundEl, this.alignment);
30048     },
30049
30050     /**
30051      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30052      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30053      */
30054     completeEdit : function(remainVisible){
30055         if(!this.editing){
30056             return;
30057         }
30058         var v = this.getValue();
30059         if(this.revertInvalid !== false && !this.field.isValid()){
30060             v = this.startValue;
30061             this.cancelEdit(true);
30062         }
30063         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30064             this.editing = false;
30065             this.hide();
30066             return;
30067         }
30068         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30069             this.editing = false;
30070             if(this.updateEl && this.boundEl){
30071                 this.boundEl.update(v);
30072             }
30073             if(remainVisible !== true){
30074                 this.hide();
30075             }
30076             this.fireEvent("complete", this, v, this.startValue);
30077         }
30078     },
30079
30080     // private
30081     onShow : function(){
30082         this.el.show();
30083         if(this.hideEl !== false){
30084             this.boundEl.hide();
30085         }
30086         this.field.show();
30087         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30088             this.fixIEFocus = true;
30089             this.deferredFocus.defer(50, this);
30090         }else{
30091             this.field.focus();
30092         }
30093         this.fireEvent("startedit", this.boundEl, this.startValue);
30094     },
30095
30096     deferredFocus : function(){
30097         if(this.editing){
30098             this.field.focus();
30099         }
30100     },
30101
30102     /**
30103      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30104      * reverted to the original starting value.
30105      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30106      * cancel (defaults to false)
30107      */
30108     cancelEdit : function(remainVisible){
30109         if(this.editing){
30110             this.setValue(this.startValue);
30111             if(remainVisible !== true){
30112                 this.hide();
30113             }
30114         }
30115     },
30116
30117     // private
30118     onBlur : function(){
30119         if(this.allowBlur !== true && this.editing){
30120             this.completeEdit();
30121         }
30122     },
30123
30124     // private
30125     onHide : function(){
30126         if(this.editing){
30127             this.completeEdit();
30128             return;
30129         }
30130         this.field.blur();
30131         if(this.field.collapse){
30132             this.field.collapse();
30133         }
30134         this.el.hide();
30135         if(this.hideEl !== false){
30136             this.boundEl.show();
30137         }
30138         if(Roo.QuickTips){
30139             Roo.QuickTips.enable();
30140         }
30141     },
30142
30143     /**
30144      * Sets the data value of the editor
30145      * @param {Mixed} value Any valid value supported by the underlying field
30146      */
30147     setValue : function(v){
30148         this.field.setValue(v);
30149     },
30150
30151     /**
30152      * Gets the data value of the editor
30153      * @return {Mixed} The data value
30154      */
30155     getValue : function(){
30156         return this.field.getValue();
30157     }
30158 });/*
30159  * Based on:
30160  * Ext JS Library 1.1.1
30161  * Copyright(c) 2006-2007, Ext JS, LLC.
30162  *
30163  * Originally Released Under LGPL - original licence link has changed is not relivant.
30164  *
30165  * Fork - LGPL
30166  * <script type="text/javascript">
30167  */
30168  
30169 /**
30170  * @class Roo.BasicDialog
30171  * @extends Roo.util.Observable
30172  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30173  * <pre><code>
30174 var dlg = new Roo.BasicDialog("my-dlg", {
30175     height: 200,
30176     width: 300,
30177     minHeight: 100,
30178     minWidth: 150,
30179     modal: true,
30180     proxyDrag: true,
30181     shadow: true
30182 });
30183 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30184 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30185 dlg.addButton('Cancel', dlg.hide, dlg);
30186 dlg.show();
30187 </code></pre>
30188   <b>A Dialog should always be a direct child of the body element.</b>
30189  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30190  * @cfg {String} title Default text to display in the title bar (defaults to null)
30191  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30192  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30193  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30194  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30195  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30196  * (defaults to null with no animation)
30197  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30198  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30199  * property for valid values (defaults to 'all')
30200  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30201  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30202  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30203  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30204  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30205  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30206  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30207  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30208  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30209  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30210  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30211  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30212  * draggable = true (defaults to false)
30213  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30214  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30215  * shadow (defaults to false)
30216  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30217  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30218  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30219  * @cfg {Array} buttons Array of buttons
30220  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30221  * @constructor
30222  * Create a new BasicDialog.
30223  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30224  * @param {Object} config Configuration options
30225  */
30226 Roo.BasicDialog = function(el, config){
30227     this.el = Roo.get(el);
30228     var dh = Roo.DomHelper;
30229     if(!this.el && config && config.autoCreate){
30230         if(typeof config.autoCreate == "object"){
30231             if(!config.autoCreate.id){
30232                 config.autoCreate.id = el;
30233             }
30234             this.el = dh.append(document.body,
30235                         config.autoCreate, true);
30236         }else{
30237             this.el = dh.append(document.body,
30238                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30239         }
30240     }
30241     el = this.el;
30242     el.setDisplayed(true);
30243     el.hide = this.hideAction;
30244     this.id = el.id;
30245     el.addClass("x-dlg");
30246
30247     Roo.apply(this, config);
30248
30249     this.proxy = el.createProxy("x-dlg-proxy");
30250     this.proxy.hide = this.hideAction;
30251     this.proxy.setOpacity(.5);
30252     this.proxy.hide();
30253
30254     if(config.width){
30255         el.setWidth(config.width);
30256     }
30257     if(config.height){
30258         el.setHeight(config.height);
30259     }
30260     this.size = el.getSize();
30261     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30262         this.xy = [config.x,config.y];
30263     }else{
30264         this.xy = el.getCenterXY(true);
30265     }
30266     /** The header element @type Roo.Element */
30267     this.header = el.child("> .x-dlg-hd");
30268     /** The body element @type Roo.Element */
30269     this.body = el.child("> .x-dlg-bd");
30270     /** The footer element @type Roo.Element */
30271     this.footer = el.child("> .x-dlg-ft");
30272
30273     if(!this.header){
30274         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30275     }
30276     if(!this.body){
30277         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30278     }
30279
30280     this.header.unselectable();
30281     if(this.title){
30282         this.header.update(this.title);
30283     }
30284     // this element allows the dialog to be focused for keyboard event
30285     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30286     this.focusEl.swallowEvent("click", true);
30287
30288     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30289
30290     // wrap the body and footer for special rendering
30291     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30292     if(this.footer){
30293         this.bwrap.dom.appendChild(this.footer.dom);
30294     }
30295
30296     this.bg = this.el.createChild({
30297         tag: "div", cls:"x-dlg-bg",
30298         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30299     });
30300     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30301
30302
30303     if(this.autoScroll !== false && !this.autoTabs){
30304         this.body.setStyle("overflow", "auto");
30305     }
30306
30307     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30308
30309     if(this.closable !== false){
30310         this.el.addClass("x-dlg-closable");
30311         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30312         this.close.on("click", this.closeClick, this);
30313         this.close.addClassOnOver("x-dlg-close-over");
30314     }
30315     if(this.collapsible !== false){
30316         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30317         this.collapseBtn.on("click", this.collapseClick, this);
30318         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30319         this.header.on("dblclick", this.collapseClick, this);
30320     }
30321     if(this.resizable !== false){
30322         this.el.addClass("x-dlg-resizable");
30323         this.resizer = new Roo.Resizable(el, {
30324             minWidth: this.minWidth || 80,
30325             minHeight:this.minHeight || 80,
30326             handles: this.resizeHandles || "all",
30327             pinned: true
30328         });
30329         this.resizer.on("beforeresize", this.beforeResize, this);
30330         this.resizer.on("resize", this.onResize, this);
30331     }
30332     if(this.draggable !== false){
30333         el.addClass("x-dlg-draggable");
30334         if (!this.proxyDrag) {
30335             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30336         }
30337         else {
30338             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30339         }
30340         dd.setHandleElId(this.header.id);
30341         dd.endDrag = this.endMove.createDelegate(this);
30342         dd.startDrag = this.startMove.createDelegate(this);
30343         dd.onDrag = this.onDrag.createDelegate(this);
30344         dd.scroll = false;
30345         this.dd = dd;
30346     }
30347     if(this.modal){
30348         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30349         this.mask.enableDisplayMode("block");
30350         this.mask.hide();
30351         this.el.addClass("x-dlg-modal");
30352     }
30353     if(this.shadow){
30354         this.shadow = new Roo.Shadow({
30355             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30356             offset : this.shadowOffset
30357         });
30358     }else{
30359         this.shadowOffset = 0;
30360     }
30361     if(Roo.useShims && this.shim !== false){
30362         this.shim = this.el.createShim();
30363         this.shim.hide = this.hideAction;
30364         this.shim.hide();
30365     }else{
30366         this.shim = false;
30367     }
30368     if(this.autoTabs){
30369         this.initTabs();
30370     }
30371     if (this.buttons) { 
30372         var bts= this.buttons;
30373         this.buttons = [];
30374         Roo.each(bts, function(b) {
30375             this.addButton(b);
30376         }, this);
30377     }
30378     
30379     
30380     this.addEvents({
30381         /**
30382          * @event keydown
30383          * Fires when a key is pressed
30384          * @param {Roo.BasicDialog} this
30385          * @param {Roo.EventObject} e
30386          */
30387         "keydown" : true,
30388         /**
30389          * @event move
30390          * Fires when this dialog is moved by the user.
30391          * @param {Roo.BasicDialog} this
30392          * @param {Number} x The new page X
30393          * @param {Number} y The new page Y
30394          */
30395         "move" : true,
30396         /**
30397          * @event resize
30398          * Fires when this dialog is resized by the user.
30399          * @param {Roo.BasicDialog} this
30400          * @param {Number} width The new width
30401          * @param {Number} height The new height
30402          */
30403         "resize" : true,
30404         /**
30405          * @event beforehide
30406          * Fires before this dialog is hidden.
30407          * @param {Roo.BasicDialog} this
30408          */
30409         "beforehide" : true,
30410         /**
30411          * @event hide
30412          * Fires when this dialog is hidden.
30413          * @param {Roo.BasicDialog} this
30414          */
30415         "hide" : true,
30416         /**
30417          * @event beforeshow
30418          * Fires before this dialog is shown.
30419          * @param {Roo.BasicDialog} this
30420          */
30421         "beforeshow" : true,
30422         /**
30423          * @event show
30424          * Fires when this dialog is shown.
30425          * @param {Roo.BasicDialog} this
30426          */
30427         "show" : true
30428     });
30429     el.on("keydown", this.onKeyDown, this);
30430     el.on("mousedown", this.toFront, this);
30431     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30432     this.el.hide();
30433     Roo.DialogManager.register(this);
30434     Roo.BasicDialog.superclass.constructor.call(this);
30435 };
30436
30437 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30438     shadowOffset: Roo.isIE ? 6 : 5,
30439     minHeight: 80,
30440     minWidth: 200,
30441     minButtonWidth: 75,
30442     defaultButton: null,
30443     buttonAlign: "right",
30444     tabTag: 'div',
30445     firstShow: true,
30446
30447     /**
30448      * Sets the dialog title text
30449      * @param {String} text The title text to display
30450      * @return {Roo.BasicDialog} this
30451      */
30452     setTitle : function(text){
30453         this.header.update(text);
30454         return this;
30455     },
30456
30457     // private
30458     closeClick : function(){
30459         this.hide();
30460     },
30461
30462     // private
30463     collapseClick : function(){
30464         this[this.collapsed ? "expand" : "collapse"]();
30465     },
30466
30467     /**
30468      * Collapses the dialog to its minimized state (only the title bar is visible).
30469      * Equivalent to the user clicking the collapse dialog button.
30470      */
30471     collapse : function(){
30472         if(!this.collapsed){
30473             this.collapsed = true;
30474             this.el.addClass("x-dlg-collapsed");
30475             this.restoreHeight = this.el.getHeight();
30476             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30477         }
30478     },
30479
30480     /**
30481      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30482      * clicking the expand dialog button.
30483      */
30484     expand : function(){
30485         if(this.collapsed){
30486             this.collapsed = false;
30487             this.el.removeClass("x-dlg-collapsed");
30488             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30489         }
30490     },
30491
30492     /**
30493      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30494      * @return {Roo.TabPanel} The tabs component
30495      */
30496     initTabs : function(){
30497         var tabs = this.getTabs();
30498         while(tabs.getTab(0)){
30499             tabs.removeTab(0);
30500         }
30501         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30502             var dom = el.dom;
30503             tabs.addTab(Roo.id(dom), dom.title);
30504             dom.title = "";
30505         });
30506         tabs.activate(0);
30507         return tabs;
30508     },
30509
30510     // private
30511     beforeResize : function(){
30512         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30513     },
30514
30515     // private
30516     onResize : function(){
30517         this.refreshSize();
30518         this.syncBodyHeight();
30519         this.adjustAssets();
30520         this.focus();
30521         this.fireEvent("resize", this, this.size.width, this.size.height);
30522     },
30523
30524     // private
30525     onKeyDown : function(e){
30526         if(this.isVisible()){
30527             this.fireEvent("keydown", this, e);
30528         }
30529     },
30530
30531     /**
30532      * Resizes the dialog.
30533      * @param {Number} width
30534      * @param {Number} height
30535      * @return {Roo.BasicDialog} this
30536      */
30537     resizeTo : function(width, height){
30538         this.el.setSize(width, height);
30539         this.size = {width: width, height: height};
30540         this.syncBodyHeight();
30541         if(this.fixedcenter){
30542             this.center();
30543         }
30544         if(this.isVisible()){
30545             this.constrainXY();
30546             this.adjustAssets();
30547         }
30548         this.fireEvent("resize", this, width, height);
30549         return this;
30550     },
30551
30552
30553     /**
30554      * Resizes the dialog to fit the specified content size.
30555      * @param {Number} width
30556      * @param {Number} height
30557      * @return {Roo.BasicDialog} this
30558      */
30559     setContentSize : function(w, h){
30560         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30561         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30562         //if(!this.el.isBorderBox()){
30563             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30564             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30565         //}
30566         if(this.tabs){
30567             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30568             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30569         }
30570         this.resizeTo(w, h);
30571         return this;
30572     },
30573
30574     /**
30575      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30576      * executed in response to a particular key being pressed while the dialog is active.
30577      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30578      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30579      * @param {Function} fn The function to call
30580      * @param {Object} scope (optional) The scope of the function
30581      * @return {Roo.BasicDialog} this
30582      */
30583     addKeyListener : function(key, fn, scope){
30584         var keyCode, shift, ctrl, alt;
30585         if(typeof key == "object" && !(key instanceof Array)){
30586             keyCode = key["key"];
30587             shift = key["shift"];
30588             ctrl = key["ctrl"];
30589             alt = key["alt"];
30590         }else{
30591             keyCode = key;
30592         }
30593         var handler = function(dlg, e){
30594             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30595                 var k = e.getKey();
30596                 if(keyCode instanceof Array){
30597                     for(var i = 0, len = keyCode.length; i < len; i++){
30598                         if(keyCode[i] == k){
30599                           fn.call(scope || window, dlg, k, e);
30600                           return;
30601                         }
30602                     }
30603                 }else{
30604                     if(k == keyCode){
30605                         fn.call(scope || window, dlg, k, e);
30606                     }
30607                 }
30608             }
30609         };
30610         this.on("keydown", handler);
30611         return this;
30612     },
30613
30614     /**
30615      * Returns the TabPanel component (creates it if it doesn't exist).
30616      * Note: If you wish to simply check for the existence of tabs without creating them,
30617      * check for a null 'tabs' property.
30618      * @return {Roo.TabPanel} The tabs component
30619      */
30620     getTabs : function(){
30621         if(!this.tabs){
30622             this.el.addClass("x-dlg-auto-tabs");
30623             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30624             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30625         }
30626         return this.tabs;
30627     },
30628
30629     /**
30630      * Adds a button to the footer section of the dialog.
30631      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30632      * object or a valid Roo.DomHelper element config
30633      * @param {Function} handler The function called when the button is clicked
30634      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30635      * @return {Roo.Button} The new button
30636      */
30637     addButton : function(config, handler, scope){
30638         var dh = Roo.DomHelper;
30639         if(!this.footer){
30640             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30641         }
30642         if(!this.btnContainer){
30643             var tb = this.footer.createChild({
30644
30645                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30646                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30647             }, null, true);
30648             this.btnContainer = tb.firstChild.firstChild.firstChild;
30649         }
30650         var bconfig = {
30651             handler: handler,
30652             scope: scope,
30653             minWidth: this.minButtonWidth,
30654             hideParent:true
30655         };
30656         if(typeof config == "string"){
30657             bconfig.text = config;
30658         }else{
30659             if(config.tag){
30660                 bconfig.dhconfig = config;
30661             }else{
30662                 Roo.apply(bconfig, config);
30663             }
30664         }
30665         var fc = false;
30666         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30667             bconfig.position = Math.max(0, bconfig.position);
30668             fc = this.btnContainer.childNodes[bconfig.position];
30669         }
30670          
30671         var btn = new Roo.Button(
30672             fc ? 
30673                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30674                 : this.btnContainer.appendChild(document.createElement("td")),
30675             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30676             bconfig
30677         );
30678         this.syncBodyHeight();
30679         if(!this.buttons){
30680             /**
30681              * Array of all the buttons that have been added to this dialog via addButton
30682              * @type Array
30683              */
30684             this.buttons = [];
30685         }
30686         this.buttons.push(btn);
30687         return btn;
30688     },
30689
30690     /**
30691      * Sets the default button to be focused when the dialog is displayed.
30692      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30693      * @return {Roo.BasicDialog} this
30694      */
30695     setDefaultButton : function(btn){
30696         this.defaultButton = btn;
30697         return this;
30698     },
30699
30700     // private
30701     getHeaderFooterHeight : function(safe){
30702         var height = 0;
30703         if(this.header){
30704            height += this.header.getHeight();
30705         }
30706         if(this.footer){
30707            var fm = this.footer.getMargins();
30708             height += (this.footer.getHeight()+fm.top+fm.bottom);
30709         }
30710         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30711         height += this.centerBg.getPadding("tb");
30712         return height;
30713     },
30714
30715     // private
30716     syncBodyHeight : function()
30717     {
30718         var bd = this.body, // the text
30719             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30720             bw = this.bwrap;
30721         var height = this.size.height - this.getHeaderFooterHeight(false);
30722         bd.setHeight(height-bd.getMargins("tb"));
30723         var hh = this.header.getHeight();
30724         var h = this.size.height-hh;
30725         cb.setHeight(h);
30726         
30727         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30728         bw.setHeight(h-cb.getPadding("tb"));
30729         
30730         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30731         bd.setWidth(bw.getWidth(true));
30732         if(this.tabs){
30733             this.tabs.syncHeight();
30734             if(Roo.isIE){
30735                 this.tabs.el.repaint();
30736             }
30737         }
30738     },
30739
30740     /**
30741      * Restores the previous state of the dialog if Roo.state is configured.
30742      * @return {Roo.BasicDialog} this
30743      */
30744     restoreState : function(){
30745         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30746         if(box && box.width){
30747             this.xy = [box.x, box.y];
30748             this.resizeTo(box.width, box.height);
30749         }
30750         return this;
30751     },
30752
30753     // private
30754     beforeShow : function(){
30755         this.expand();
30756         if(this.fixedcenter){
30757             this.xy = this.el.getCenterXY(true);
30758         }
30759         if(this.modal){
30760             Roo.get(document.body).addClass("x-body-masked");
30761             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30762             this.mask.show();
30763         }
30764         this.constrainXY();
30765     },
30766
30767     // private
30768     animShow : function(){
30769         var b = Roo.get(this.animateTarget).getBox();
30770         this.proxy.setSize(b.width, b.height);
30771         this.proxy.setLocation(b.x, b.y);
30772         this.proxy.show();
30773         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30774                     true, .35, this.showEl.createDelegate(this));
30775     },
30776
30777     /**
30778      * Shows the dialog.
30779      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30780      * @return {Roo.BasicDialog} this
30781      */
30782     show : function(animateTarget){
30783         if (this.fireEvent("beforeshow", this) === false){
30784             return;
30785         }
30786         if(this.syncHeightBeforeShow){
30787             this.syncBodyHeight();
30788         }else if(this.firstShow){
30789             this.firstShow = false;
30790             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30791         }
30792         this.animateTarget = animateTarget || this.animateTarget;
30793         if(!this.el.isVisible()){
30794             this.beforeShow();
30795             if(this.animateTarget && Roo.get(this.animateTarget)){
30796                 this.animShow();
30797             }else{
30798                 this.showEl();
30799             }
30800         }
30801         return this;
30802     },
30803
30804     // private
30805     showEl : function(){
30806         this.proxy.hide();
30807         this.el.setXY(this.xy);
30808         this.el.show();
30809         this.adjustAssets(true);
30810         this.toFront();
30811         this.focus();
30812         // IE peekaboo bug - fix found by Dave Fenwick
30813         if(Roo.isIE){
30814             this.el.repaint();
30815         }
30816         this.fireEvent("show", this);
30817     },
30818
30819     /**
30820      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30821      * dialog itself will receive focus.
30822      */
30823     focus : function(){
30824         if(this.defaultButton){
30825             this.defaultButton.focus();
30826         }else{
30827             this.focusEl.focus();
30828         }
30829     },
30830
30831     // private
30832     constrainXY : function(){
30833         if(this.constraintoviewport !== false){
30834             if(!this.viewSize){
30835                 if(this.container){
30836                     var s = this.container.getSize();
30837                     this.viewSize = [s.width, s.height];
30838                 }else{
30839                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30840                 }
30841             }
30842             var s = Roo.get(this.container||document).getScroll();
30843
30844             var x = this.xy[0], y = this.xy[1];
30845             var w = this.size.width, h = this.size.height;
30846             var vw = this.viewSize[0], vh = this.viewSize[1];
30847             // only move it if it needs it
30848             var moved = false;
30849             // first validate right/bottom
30850             if(x + w > vw+s.left){
30851                 x = vw - w;
30852                 moved = true;
30853             }
30854             if(y + h > vh+s.top){
30855                 y = vh - h;
30856                 moved = true;
30857             }
30858             // then make sure top/left isn't negative
30859             if(x < s.left){
30860                 x = s.left;
30861                 moved = true;
30862             }
30863             if(y < s.top){
30864                 y = s.top;
30865                 moved = true;
30866             }
30867             if(moved){
30868                 // cache xy
30869                 this.xy = [x, y];
30870                 if(this.isVisible()){
30871                     this.el.setLocation(x, y);
30872                     this.adjustAssets();
30873                 }
30874             }
30875         }
30876     },
30877
30878     // private
30879     onDrag : function(){
30880         if(!this.proxyDrag){
30881             this.xy = this.el.getXY();
30882             this.adjustAssets();
30883         }
30884     },
30885
30886     // private
30887     adjustAssets : function(doShow){
30888         var x = this.xy[0], y = this.xy[1];
30889         var w = this.size.width, h = this.size.height;
30890         if(doShow === true){
30891             if(this.shadow){
30892                 this.shadow.show(this.el);
30893             }
30894             if(this.shim){
30895                 this.shim.show();
30896             }
30897         }
30898         if(this.shadow && this.shadow.isVisible()){
30899             this.shadow.show(this.el);
30900         }
30901         if(this.shim && this.shim.isVisible()){
30902             this.shim.setBounds(x, y, w, h);
30903         }
30904     },
30905
30906     // private
30907     adjustViewport : function(w, h){
30908         if(!w || !h){
30909             w = Roo.lib.Dom.getViewWidth();
30910             h = Roo.lib.Dom.getViewHeight();
30911         }
30912         // cache the size
30913         this.viewSize = [w, h];
30914         if(this.modal && this.mask.isVisible()){
30915             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30916             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30917         }
30918         if(this.isVisible()){
30919             this.constrainXY();
30920         }
30921     },
30922
30923     /**
30924      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30925      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30926      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30927      */
30928     destroy : function(removeEl){
30929         if(this.isVisible()){
30930             this.animateTarget = null;
30931             this.hide();
30932         }
30933         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30934         if(this.tabs){
30935             this.tabs.destroy(removeEl);
30936         }
30937         Roo.destroy(
30938              this.shim,
30939              this.proxy,
30940              this.resizer,
30941              this.close,
30942              this.mask
30943         );
30944         if(this.dd){
30945             this.dd.unreg();
30946         }
30947         if(this.buttons){
30948            for(var i = 0, len = this.buttons.length; i < len; i++){
30949                this.buttons[i].destroy();
30950            }
30951         }
30952         this.el.removeAllListeners();
30953         if(removeEl === true){
30954             this.el.update("");
30955             this.el.remove();
30956         }
30957         Roo.DialogManager.unregister(this);
30958     },
30959
30960     // private
30961     startMove : function(){
30962         if(this.proxyDrag){
30963             this.proxy.show();
30964         }
30965         if(this.constraintoviewport !== false){
30966             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30967         }
30968     },
30969
30970     // private
30971     endMove : function(){
30972         if(!this.proxyDrag){
30973             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30974         }else{
30975             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30976             this.proxy.hide();
30977         }
30978         this.refreshSize();
30979         this.adjustAssets();
30980         this.focus();
30981         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30982     },
30983
30984     /**
30985      * Brings this dialog to the front of any other visible dialogs
30986      * @return {Roo.BasicDialog} this
30987      */
30988     toFront : function(){
30989         Roo.DialogManager.bringToFront(this);
30990         return this;
30991     },
30992
30993     /**
30994      * Sends this dialog to the back (under) of any other visible dialogs
30995      * @return {Roo.BasicDialog} this
30996      */
30997     toBack : function(){
30998         Roo.DialogManager.sendToBack(this);
30999         return this;
31000     },
31001
31002     /**
31003      * Centers this dialog in the viewport
31004      * @return {Roo.BasicDialog} this
31005      */
31006     center : function(){
31007         var xy = this.el.getCenterXY(true);
31008         this.moveTo(xy[0], xy[1]);
31009         return this;
31010     },
31011
31012     /**
31013      * Moves the dialog's top-left corner to the specified point
31014      * @param {Number} x
31015      * @param {Number} y
31016      * @return {Roo.BasicDialog} this
31017      */
31018     moveTo : function(x, y){
31019         this.xy = [x,y];
31020         if(this.isVisible()){
31021             this.el.setXY(this.xy);
31022             this.adjustAssets();
31023         }
31024         return this;
31025     },
31026
31027     /**
31028      * Aligns the dialog to the specified element
31029      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31030      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31031      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31032      * @return {Roo.BasicDialog} this
31033      */
31034     alignTo : function(element, position, offsets){
31035         this.xy = this.el.getAlignToXY(element, position, offsets);
31036         if(this.isVisible()){
31037             this.el.setXY(this.xy);
31038             this.adjustAssets();
31039         }
31040         return this;
31041     },
31042
31043     /**
31044      * Anchors an element to another element and realigns it when the window is resized.
31045      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31046      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31047      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31048      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31049      * is a number, it is used as the buffer delay (defaults to 50ms).
31050      * @return {Roo.BasicDialog} this
31051      */
31052     anchorTo : function(el, alignment, offsets, monitorScroll){
31053         var action = function(){
31054             this.alignTo(el, alignment, offsets);
31055         };
31056         Roo.EventManager.onWindowResize(action, this);
31057         var tm = typeof monitorScroll;
31058         if(tm != 'undefined'){
31059             Roo.EventManager.on(window, 'scroll', action, this,
31060                 {buffer: tm == 'number' ? monitorScroll : 50});
31061         }
31062         action.call(this);
31063         return this;
31064     },
31065
31066     /**
31067      * Returns true if the dialog is visible
31068      * @return {Boolean}
31069      */
31070     isVisible : function(){
31071         return this.el.isVisible();
31072     },
31073
31074     // private
31075     animHide : function(callback){
31076         var b = Roo.get(this.animateTarget).getBox();
31077         this.proxy.show();
31078         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31079         this.el.hide();
31080         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31081                     this.hideEl.createDelegate(this, [callback]));
31082     },
31083
31084     /**
31085      * Hides the dialog.
31086      * @param {Function} callback (optional) Function to call when the dialog is hidden
31087      * @return {Roo.BasicDialog} this
31088      */
31089     hide : function(callback){
31090         if (this.fireEvent("beforehide", this) === false){
31091             return;
31092         }
31093         if(this.shadow){
31094             this.shadow.hide();
31095         }
31096         if(this.shim) {
31097           this.shim.hide();
31098         }
31099         // sometimes animateTarget seems to get set.. causing problems...
31100         // this just double checks..
31101         if(this.animateTarget && Roo.get(this.animateTarget)) {
31102            this.animHide(callback);
31103         }else{
31104             this.el.hide();
31105             this.hideEl(callback);
31106         }
31107         return this;
31108     },
31109
31110     // private
31111     hideEl : function(callback){
31112         this.proxy.hide();
31113         if(this.modal){
31114             this.mask.hide();
31115             Roo.get(document.body).removeClass("x-body-masked");
31116         }
31117         this.fireEvent("hide", this);
31118         if(typeof callback == "function"){
31119             callback();
31120         }
31121     },
31122
31123     // private
31124     hideAction : function(){
31125         this.setLeft("-10000px");
31126         this.setTop("-10000px");
31127         this.setStyle("visibility", "hidden");
31128     },
31129
31130     // private
31131     refreshSize : function(){
31132         this.size = this.el.getSize();
31133         this.xy = this.el.getXY();
31134         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31135     },
31136
31137     // private
31138     // z-index is managed by the DialogManager and may be overwritten at any time
31139     setZIndex : function(index){
31140         if(this.modal){
31141             this.mask.setStyle("z-index", index);
31142         }
31143         if(this.shim){
31144             this.shim.setStyle("z-index", ++index);
31145         }
31146         if(this.shadow){
31147             this.shadow.setZIndex(++index);
31148         }
31149         this.el.setStyle("z-index", ++index);
31150         if(this.proxy){
31151             this.proxy.setStyle("z-index", ++index);
31152         }
31153         if(this.resizer){
31154             this.resizer.proxy.setStyle("z-index", ++index);
31155         }
31156
31157         this.lastZIndex = index;
31158     },
31159
31160     /**
31161      * Returns the element for this dialog
31162      * @return {Roo.Element} The underlying dialog Element
31163      */
31164     getEl : function(){
31165         return this.el;
31166     }
31167 });
31168
31169 /**
31170  * @class Roo.DialogManager
31171  * Provides global access to BasicDialogs that have been created and
31172  * support for z-indexing (layering) multiple open dialogs.
31173  */
31174 Roo.DialogManager = function(){
31175     var list = {};
31176     var accessList = [];
31177     var front = null;
31178
31179     // private
31180     var sortDialogs = function(d1, d2){
31181         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31182     };
31183
31184     // private
31185     var orderDialogs = function(){
31186         accessList.sort(sortDialogs);
31187         var seed = Roo.DialogManager.zseed;
31188         for(var i = 0, len = accessList.length; i < len; i++){
31189             var dlg = accessList[i];
31190             if(dlg){
31191                 dlg.setZIndex(seed + (i*10));
31192             }
31193         }
31194     };
31195
31196     return {
31197         /**
31198          * The starting z-index for BasicDialogs (defaults to 9000)
31199          * @type Number The z-index value
31200          */
31201         zseed : 9000,
31202
31203         // private
31204         register : function(dlg){
31205             list[dlg.id] = dlg;
31206             accessList.push(dlg);
31207         },
31208
31209         // private
31210         unregister : function(dlg){
31211             delete list[dlg.id];
31212             var i=0;
31213             var len=0;
31214             if(!accessList.indexOf){
31215                 for(  i = 0, len = accessList.length; i < len; i++){
31216                     if(accessList[i] == dlg){
31217                         accessList.splice(i, 1);
31218                         return;
31219                     }
31220                 }
31221             }else{
31222                  i = accessList.indexOf(dlg);
31223                 if(i != -1){
31224                     accessList.splice(i, 1);
31225                 }
31226             }
31227         },
31228
31229         /**
31230          * Gets a registered dialog by id
31231          * @param {String/Object} id The id of the dialog or a dialog
31232          * @return {Roo.BasicDialog} this
31233          */
31234         get : function(id){
31235             return typeof id == "object" ? id : list[id];
31236         },
31237
31238         /**
31239          * Brings the specified dialog to the front
31240          * @param {String/Object} dlg The id of the dialog or a dialog
31241          * @return {Roo.BasicDialog} this
31242          */
31243         bringToFront : function(dlg){
31244             dlg = this.get(dlg);
31245             if(dlg != front){
31246                 front = dlg;
31247                 dlg._lastAccess = new Date().getTime();
31248                 orderDialogs();
31249             }
31250             return dlg;
31251         },
31252
31253         /**
31254          * Sends the specified dialog to the back
31255          * @param {String/Object} dlg The id of the dialog or a dialog
31256          * @return {Roo.BasicDialog} this
31257          */
31258         sendToBack : function(dlg){
31259             dlg = this.get(dlg);
31260             dlg._lastAccess = -(new Date().getTime());
31261             orderDialogs();
31262             return dlg;
31263         },
31264
31265         /**
31266          * Hides all dialogs
31267          */
31268         hideAll : function(){
31269             for(var id in list){
31270                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31271                     list[id].hide();
31272                 }
31273             }
31274         }
31275     };
31276 }();
31277
31278 /**
31279  * @class Roo.LayoutDialog
31280  * @extends Roo.BasicDialog
31281  * Dialog which provides adjustments for working with a layout in a Dialog.
31282  * Add your necessary layout config options to the dialog's config.<br>
31283  * Example usage (including a nested layout):
31284  * <pre><code>
31285 if(!dialog){
31286     dialog = new Roo.LayoutDialog("download-dlg", {
31287         modal: true,
31288         width:600,
31289         height:450,
31290         shadow:true,
31291         minWidth:500,
31292         minHeight:350,
31293         autoTabs:true,
31294         proxyDrag:true,
31295         // layout config merges with the dialog config
31296         center:{
31297             tabPosition: "top",
31298             alwaysShowTabs: true
31299         }
31300     });
31301     dialog.addKeyListener(27, dialog.hide, dialog);
31302     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31303     dialog.addButton("Build It!", this.getDownload, this);
31304
31305     // we can even add nested layouts
31306     var innerLayout = new Roo.BorderLayout("dl-inner", {
31307         east: {
31308             initialSize: 200,
31309             autoScroll:true,
31310             split:true
31311         },
31312         center: {
31313             autoScroll:true
31314         }
31315     });
31316     innerLayout.beginUpdate();
31317     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31318     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31319     innerLayout.endUpdate(true);
31320
31321     var layout = dialog.getLayout();
31322     layout.beginUpdate();
31323     layout.add("center", new Roo.ContentPanel("standard-panel",
31324                         {title: "Download the Source", fitToFrame:true}));
31325     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31326                {title: "Build your own roo.js"}));
31327     layout.getRegion("center").showPanel(sp);
31328     layout.endUpdate();
31329 }
31330 </code></pre>
31331     * @constructor
31332     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31333     * @param {Object} config configuration options
31334   */
31335 Roo.LayoutDialog = function(el, cfg){
31336     
31337     var config=  cfg;
31338     if (typeof(cfg) == 'undefined') {
31339         config = Roo.apply({}, el);
31340         // not sure why we use documentElement here.. - it should always be body.
31341         // IE7 borks horribly if we use documentElement.
31342         // webkit also does not like documentElement - it creates a body element...
31343         el = Roo.get( document.body || document.documentElement ).createChild();
31344         //config.autoCreate = true;
31345     }
31346     
31347     
31348     config.autoTabs = false;
31349     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31350     this.body.setStyle({overflow:"hidden", position:"relative"});
31351     this.layout = new Roo.BorderLayout(this.body.dom, config);
31352     this.layout.monitorWindowResize = false;
31353     this.el.addClass("x-dlg-auto-layout");
31354     // fix case when center region overwrites center function
31355     this.center = Roo.BasicDialog.prototype.center;
31356     this.on("show", this.layout.layout, this.layout, true);
31357     if (config.items) {
31358         var xitems = config.items;
31359         delete config.items;
31360         Roo.each(xitems, this.addxtype, this);
31361     }
31362     
31363     
31364 };
31365 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31366     /**
31367      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31368      * @deprecated
31369      */
31370     endUpdate : function(){
31371         this.layout.endUpdate();
31372     },
31373
31374     /**
31375      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31376      *  @deprecated
31377      */
31378     beginUpdate : function(){
31379         this.layout.beginUpdate();
31380     },
31381
31382     /**
31383      * Get the BorderLayout for this dialog
31384      * @return {Roo.BorderLayout}
31385      */
31386     getLayout : function(){
31387         return this.layout;
31388     },
31389
31390     showEl : function(){
31391         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31392         if(Roo.isIE7){
31393             this.layout.layout();
31394         }
31395     },
31396
31397     // private
31398     // Use the syncHeightBeforeShow config option to control this automatically
31399     syncBodyHeight : function(){
31400         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31401         if(this.layout){this.layout.layout();}
31402     },
31403     
31404       /**
31405      * Add an xtype element (actually adds to the layout.)
31406      * @return {Object} xdata xtype object data.
31407      */
31408     
31409     addxtype : function(c) {
31410         return this.layout.addxtype(c);
31411     }
31412 });/*
31413  * Based on:
31414  * Ext JS Library 1.1.1
31415  * Copyright(c) 2006-2007, Ext JS, LLC.
31416  *
31417  * Originally Released Under LGPL - original licence link has changed is not relivant.
31418  *
31419  * Fork - LGPL
31420  * <script type="text/javascript">
31421  */
31422  
31423 /**
31424  * @class Roo.MessageBox
31425  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31426  * Example usage:
31427  *<pre><code>
31428 // Basic alert:
31429 Roo.Msg.alert('Status', 'Changes saved successfully.');
31430
31431 // Prompt for user data:
31432 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31433     if (btn == 'ok'){
31434         // process text value...
31435     }
31436 });
31437
31438 // Show a dialog using config options:
31439 Roo.Msg.show({
31440    title:'Save Changes?',
31441    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31442    buttons: Roo.Msg.YESNOCANCEL,
31443    fn: processResult,
31444    animEl: 'elId'
31445 });
31446 </code></pre>
31447  * @singleton
31448  */
31449 Roo.MessageBox = function(){
31450     var dlg, opt, mask, waitTimer;
31451     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31452     var buttons, activeTextEl, bwidth;
31453
31454     // private
31455     var handleButton = function(button){
31456         dlg.hide();
31457         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31458     };
31459
31460     // private
31461     var handleHide = function(){
31462         if(opt && opt.cls){
31463             dlg.el.removeClass(opt.cls);
31464         }
31465         if(waitTimer){
31466             Roo.TaskMgr.stop(waitTimer);
31467             waitTimer = null;
31468         }
31469     };
31470
31471     // private
31472     var updateButtons = function(b){
31473         var width = 0;
31474         if(!b){
31475             buttons["ok"].hide();
31476             buttons["cancel"].hide();
31477             buttons["yes"].hide();
31478             buttons["no"].hide();
31479             dlg.footer.dom.style.display = 'none';
31480             return width;
31481         }
31482         dlg.footer.dom.style.display = '';
31483         for(var k in buttons){
31484             if(typeof buttons[k] != "function"){
31485                 if(b[k]){
31486                     buttons[k].show();
31487                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31488                     width += buttons[k].el.getWidth()+15;
31489                 }else{
31490                     buttons[k].hide();
31491                 }
31492             }
31493         }
31494         return width;
31495     };
31496
31497     // private
31498     var handleEsc = function(d, k, e){
31499         if(opt && opt.closable !== false){
31500             dlg.hide();
31501         }
31502         if(e){
31503             e.stopEvent();
31504         }
31505     };
31506
31507     return {
31508         /**
31509          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31510          * @return {Roo.BasicDialog} The BasicDialog element
31511          */
31512         getDialog : function(){
31513            if(!dlg){
31514                 dlg = new Roo.BasicDialog("x-msg-box", {
31515                     autoCreate : true,
31516                     shadow: true,
31517                     draggable: true,
31518                     resizable:false,
31519                     constraintoviewport:false,
31520                     fixedcenter:true,
31521                     collapsible : false,
31522                     shim:true,
31523                     modal: true,
31524                     width:400, height:100,
31525                     buttonAlign:"center",
31526                     closeClick : function(){
31527                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31528                             handleButton("no");
31529                         }else{
31530                             handleButton("cancel");
31531                         }
31532                     }
31533                 });
31534                 dlg.on("hide", handleHide);
31535                 mask = dlg.mask;
31536                 dlg.addKeyListener(27, handleEsc);
31537                 buttons = {};
31538                 var bt = this.buttonText;
31539                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31540                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31541                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31542                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31543                 bodyEl = dlg.body.createChild({
31544
31545                     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>'
31546                 });
31547                 msgEl = bodyEl.dom.firstChild;
31548                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31549                 textboxEl.enableDisplayMode();
31550                 textboxEl.addKeyListener([10,13], function(){
31551                     if(dlg.isVisible() && opt && opt.buttons){
31552                         if(opt.buttons.ok){
31553                             handleButton("ok");
31554                         }else if(opt.buttons.yes){
31555                             handleButton("yes");
31556                         }
31557                     }
31558                 });
31559                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31560                 textareaEl.enableDisplayMode();
31561                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31562                 progressEl.enableDisplayMode();
31563                 var pf = progressEl.dom.firstChild;
31564                 if (pf) {
31565                     pp = Roo.get(pf.firstChild);
31566                     pp.setHeight(pf.offsetHeight);
31567                 }
31568                 
31569             }
31570             return dlg;
31571         },
31572
31573         /**
31574          * Updates the message box body text
31575          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31576          * the XHTML-compliant non-breaking space character '&amp;#160;')
31577          * @return {Roo.MessageBox} This message box
31578          */
31579         updateText : function(text){
31580             if(!dlg.isVisible() && !opt.width){
31581                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31582             }
31583             msgEl.innerHTML = text || '&#160;';
31584       
31585             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31586             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31587             var w = Math.max(
31588                     Math.min(opt.width || cw , this.maxWidth), 
31589                     Math.max(opt.minWidth || this.minWidth, bwidth)
31590             );
31591             if(opt.prompt){
31592                 activeTextEl.setWidth(w);
31593             }
31594             if(dlg.isVisible()){
31595                 dlg.fixedcenter = false;
31596             }
31597             // to big, make it scroll. = But as usual stupid IE does not support
31598             // !important..
31599             
31600             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31601                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31602                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31603             } else {
31604                 bodyEl.dom.style.height = '';
31605                 bodyEl.dom.style.overflowY = '';
31606             }
31607             if (cw > w) {
31608                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31609             } else {
31610                 bodyEl.dom.style.overflowX = '';
31611             }
31612             
31613             dlg.setContentSize(w, bodyEl.getHeight());
31614             if(dlg.isVisible()){
31615                 dlg.fixedcenter = true;
31616             }
31617             return this;
31618         },
31619
31620         /**
31621          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31622          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31623          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31624          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31625          * @return {Roo.MessageBox} This message box
31626          */
31627         updateProgress : function(value, text){
31628             if(text){
31629                 this.updateText(text);
31630             }
31631             if (pp) { // weird bug on my firefox - for some reason this is not defined
31632                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31633             }
31634             return this;
31635         },        
31636
31637         /**
31638          * Returns true if the message box is currently displayed
31639          * @return {Boolean} True if the message box is visible, else false
31640          */
31641         isVisible : function(){
31642             return dlg && dlg.isVisible();  
31643         },
31644
31645         /**
31646          * Hides the message box if it is displayed
31647          */
31648         hide : function(){
31649             if(this.isVisible()){
31650                 dlg.hide();
31651             }  
31652         },
31653
31654         /**
31655          * Displays a new message box, or reinitializes an existing message box, based on the config options
31656          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31657          * The following config object properties are supported:
31658          * <pre>
31659 Property    Type             Description
31660 ----------  ---------------  ------------------------------------------------------------------------------------
31661 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31662                                    closes (defaults to undefined)
31663 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31664                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31665 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31666                                    progress and wait dialogs will ignore this property and always hide the
31667                                    close button as they can only be closed programmatically.
31668 cls               String           A custom CSS class to apply to the message box element
31669 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31670                                    displayed (defaults to 75)
31671 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31672                                    function will be btn (the name of the button that was clicked, if applicable,
31673                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31674                                    Progress and wait dialogs will ignore this option since they do not respond to
31675                                    user actions and can only be closed programmatically, so any required function
31676                                    should be called by the same code after it closes the dialog.
31677 icon              String           A CSS class that provides a background image to be used as an icon for
31678                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31679 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31680 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31681 modal             Boolean          False to allow user interaction with the page while the message box is
31682                                    displayed (defaults to true)
31683 msg               String           A string that will replace the existing message box body text (defaults
31684                                    to the XHTML-compliant non-breaking space character '&#160;')
31685 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31686 progress          Boolean          True to display a progress bar (defaults to false)
31687 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31688 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31689 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31690 title             String           The title text
31691 value             String           The string value to set into the active textbox element if displayed
31692 wait              Boolean          True to display a progress bar (defaults to false)
31693 width             Number           The width of the dialog in pixels
31694 </pre>
31695          *
31696          * Example usage:
31697          * <pre><code>
31698 Roo.Msg.show({
31699    title: 'Address',
31700    msg: 'Please enter your address:',
31701    width: 300,
31702    buttons: Roo.MessageBox.OKCANCEL,
31703    multiline: true,
31704    fn: saveAddress,
31705    animEl: 'addAddressBtn'
31706 });
31707 </code></pre>
31708          * @param {Object} config Configuration options
31709          * @return {Roo.MessageBox} This message box
31710          */
31711         show : function(options)
31712         {
31713             
31714             // this causes nightmares if you show one dialog after another
31715             // especially on callbacks..
31716              
31717             if(this.isVisible()){
31718                 
31719                 this.hide();
31720                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31721                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31722                 Roo.log("New Dialog Message:" +  options.msg )
31723                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31724                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31725                 
31726             }
31727             var d = this.getDialog();
31728             opt = options;
31729             d.setTitle(opt.title || "&#160;");
31730             d.close.setDisplayed(opt.closable !== false);
31731             activeTextEl = textboxEl;
31732             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31733             if(opt.prompt){
31734                 if(opt.multiline){
31735                     textboxEl.hide();
31736                     textareaEl.show();
31737                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31738                         opt.multiline : this.defaultTextHeight);
31739                     activeTextEl = textareaEl;
31740                 }else{
31741                     textboxEl.show();
31742                     textareaEl.hide();
31743                 }
31744             }else{
31745                 textboxEl.hide();
31746                 textareaEl.hide();
31747             }
31748             progressEl.setDisplayed(opt.progress === true);
31749             this.updateProgress(0);
31750             activeTextEl.dom.value = opt.value || "";
31751             if(opt.prompt){
31752                 dlg.setDefaultButton(activeTextEl);
31753             }else{
31754                 var bs = opt.buttons;
31755                 var db = null;
31756                 if(bs && bs.ok){
31757                     db = buttons["ok"];
31758                 }else if(bs && bs.yes){
31759                     db = buttons["yes"];
31760                 }
31761                 dlg.setDefaultButton(db);
31762             }
31763             bwidth = updateButtons(opt.buttons);
31764             this.updateText(opt.msg);
31765             if(opt.cls){
31766                 d.el.addClass(opt.cls);
31767             }
31768             d.proxyDrag = opt.proxyDrag === true;
31769             d.modal = opt.modal !== false;
31770             d.mask = opt.modal !== false ? mask : false;
31771             if(!d.isVisible()){
31772                 // force it to the end of the z-index stack so it gets a cursor in FF
31773                 document.body.appendChild(dlg.el.dom);
31774                 d.animateTarget = null;
31775                 d.show(options.animEl);
31776             }
31777             return this;
31778         },
31779
31780         /**
31781          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31782          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31783          * and closing the message box when the process is complete.
31784          * @param {String} title The title bar text
31785          * @param {String} msg The message box body text
31786          * @return {Roo.MessageBox} This message box
31787          */
31788         progress : function(title, msg){
31789             this.show({
31790                 title : title,
31791                 msg : msg,
31792                 buttons: false,
31793                 progress:true,
31794                 closable:false,
31795                 minWidth: this.minProgressWidth,
31796                 modal : true
31797             });
31798             return this;
31799         },
31800
31801         /**
31802          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31803          * If a callback function is passed it will be called after the user clicks the button, and the
31804          * id of the button that was clicked will be passed as the only parameter to the callback
31805          * (could also be the top-right close button).
31806          * @param {String} title The title bar text
31807          * @param {String} msg The message box body text
31808          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31809          * @param {Object} scope (optional) The scope of the callback function
31810          * @return {Roo.MessageBox} This message box
31811          */
31812         alert : function(title, msg, fn, scope){
31813             this.show({
31814                 title : title,
31815                 msg : msg,
31816                 buttons: this.OK,
31817                 fn: fn,
31818                 scope : scope,
31819                 modal : true
31820             });
31821             return this;
31822         },
31823
31824         /**
31825          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31826          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31827          * You are responsible for closing the message box when the process is complete.
31828          * @param {String} msg The message box body text
31829          * @param {String} title (optional) The title bar text
31830          * @return {Roo.MessageBox} This message box
31831          */
31832         wait : function(msg, title){
31833             this.show({
31834                 title : title,
31835                 msg : msg,
31836                 buttons: false,
31837                 closable:false,
31838                 progress:true,
31839                 modal:true,
31840                 width:300,
31841                 wait:true
31842             });
31843             waitTimer = Roo.TaskMgr.start({
31844                 run: function(i){
31845                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31846                 },
31847                 interval: 1000
31848             });
31849             return this;
31850         },
31851
31852         /**
31853          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31854          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31855          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31856          * @param {String} title The title bar text
31857          * @param {String} msg The message box body text
31858          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31859          * @param {Object} scope (optional) The scope of the callback function
31860          * @return {Roo.MessageBox} This message box
31861          */
31862         confirm : function(title, msg, fn, scope){
31863             this.show({
31864                 title : title,
31865                 msg : msg,
31866                 buttons: this.YESNO,
31867                 fn: fn,
31868                 scope : scope,
31869                 modal : true
31870             });
31871             return this;
31872         },
31873
31874         /**
31875          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31876          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31877          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31878          * (could also be the top-right close button) and the text that was entered will be passed as the two
31879          * parameters to the callback.
31880          * @param {String} title The title bar text
31881          * @param {String} msg The message box body text
31882          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31883          * @param {Object} scope (optional) The scope of the callback function
31884          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31885          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31886          * @return {Roo.MessageBox} This message box
31887          */
31888         prompt : function(title, msg, fn, scope, multiline){
31889             this.show({
31890                 title : title,
31891                 msg : msg,
31892                 buttons: this.OKCANCEL,
31893                 fn: fn,
31894                 minWidth:250,
31895                 scope : scope,
31896                 prompt:true,
31897                 multiline: multiline,
31898                 modal : true
31899             });
31900             return this;
31901         },
31902
31903         /**
31904          * Button config that displays a single OK button
31905          * @type Object
31906          */
31907         OK : {ok:true},
31908         /**
31909          * Button config that displays Yes and No buttons
31910          * @type Object
31911          */
31912         YESNO : {yes:true, no:true},
31913         /**
31914          * Button config that displays OK and Cancel buttons
31915          * @type Object
31916          */
31917         OKCANCEL : {ok:true, cancel:true},
31918         /**
31919          * Button config that displays Yes, No and Cancel buttons
31920          * @type Object
31921          */
31922         YESNOCANCEL : {yes:true, no:true, cancel:true},
31923
31924         /**
31925          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31926          * @type Number
31927          */
31928         defaultTextHeight : 75,
31929         /**
31930          * The maximum width in pixels of the message box (defaults to 600)
31931          * @type Number
31932          */
31933         maxWidth : 600,
31934         /**
31935          * The minimum width in pixels of the message box (defaults to 100)
31936          * @type Number
31937          */
31938         minWidth : 100,
31939         /**
31940          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31941          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31942          * @type Number
31943          */
31944         minProgressWidth : 250,
31945         /**
31946          * An object containing the default button text strings that can be overriden for localized language support.
31947          * Supported properties are: ok, cancel, yes and no.
31948          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31949          * @type Object
31950          */
31951         buttonText : {
31952             ok : "OK",
31953             cancel : "Cancel",
31954             yes : "Yes",
31955             no : "No"
31956         }
31957     };
31958 }();
31959
31960 /**
31961  * Shorthand for {@link Roo.MessageBox}
31962  */
31963 Roo.Msg = Roo.MessageBox;/*
31964  * Based on:
31965  * Ext JS Library 1.1.1
31966  * Copyright(c) 2006-2007, Ext JS, LLC.
31967  *
31968  * Originally Released Under LGPL - original licence link has changed is not relivant.
31969  *
31970  * Fork - LGPL
31971  * <script type="text/javascript">
31972  */
31973 /**
31974  * @class Roo.QuickTips
31975  * Provides attractive and customizable tooltips for any element.
31976  * @singleton
31977  */
31978 Roo.QuickTips = function(){
31979     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31980     var ce, bd, xy, dd;
31981     var visible = false, disabled = true, inited = false;
31982     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31983     
31984     var onOver = function(e){
31985         if(disabled){
31986             return;
31987         }
31988         var t = e.getTarget();
31989         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31990             return;
31991         }
31992         if(ce && t == ce.el){
31993             clearTimeout(hideProc);
31994             return;
31995         }
31996         if(t && tagEls[t.id]){
31997             tagEls[t.id].el = t;
31998             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31999             return;
32000         }
32001         var ttp, et = Roo.fly(t);
32002         var ns = cfg.namespace;
32003         if(tm.interceptTitles && t.title){
32004             ttp = t.title;
32005             t.qtip = ttp;
32006             t.removeAttribute("title");
32007             e.preventDefault();
32008         }else{
32009             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32010         }
32011         if(ttp){
32012             showProc = show.defer(tm.showDelay, tm, [{
32013                 el: t, 
32014                 text: ttp, 
32015                 width: et.getAttributeNS(ns, cfg.width),
32016                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32017                 title: et.getAttributeNS(ns, cfg.title),
32018                     cls: et.getAttributeNS(ns, cfg.cls)
32019             }]);
32020         }
32021     };
32022     
32023     var onOut = function(e){
32024         clearTimeout(showProc);
32025         var t = e.getTarget();
32026         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32027             hideProc = setTimeout(hide, tm.hideDelay);
32028         }
32029     };
32030     
32031     var onMove = function(e){
32032         if(disabled){
32033             return;
32034         }
32035         xy = e.getXY();
32036         xy[1] += 18;
32037         if(tm.trackMouse && ce){
32038             el.setXY(xy);
32039         }
32040     };
32041     
32042     var onDown = function(e){
32043         clearTimeout(showProc);
32044         clearTimeout(hideProc);
32045         if(!e.within(el)){
32046             if(tm.hideOnClick){
32047                 hide();
32048                 tm.disable();
32049                 tm.enable.defer(100, tm);
32050             }
32051         }
32052     };
32053     
32054     var getPad = function(){
32055         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32056     };
32057
32058     var show = function(o){
32059         if(disabled){
32060             return;
32061         }
32062         clearTimeout(dismissProc);
32063         ce = o;
32064         if(removeCls){ // in case manually hidden
32065             el.removeClass(removeCls);
32066             removeCls = null;
32067         }
32068         if(ce.cls){
32069             el.addClass(ce.cls);
32070             removeCls = ce.cls;
32071         }
32072         if(ce.title){
32073             tipTitle.update(ce.title);
32074             tipTitle.show();
32075         }else{
32076             tipTitle.update('');
32077             tipTitle.hide();
32078         }
32079         el.dom.style.width  = tm.maxWidth+'px';
32080         //tipBody.dom.style.width = '';
32081         tipBodyText.update(o.text);
32082         var p = getPad(), w = ce.width;
32083         if(!w){
32084             var td = tipBodyText.dom;
32085             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32086             if(aw > tm.maxWidth){
32087                 w = tm.maxWidth;
32088             }else if(aw < tm.minWidth){
32089                 w = tm.minWidth;
32090             }else{
32091                 w = aw;
32092             }
32093         }
32094         //tipBody.setWidth(w);
32095         el.setWidth(parseInt(w, 10) + p);
32096         if(ce.autoHide === false){
32097             close.setDisplayed(true);
32098             if(dd){
32099                 dd.unlock();
32100             }
32101         }else{
32102             close.setDisplayed(false);
32103             if(dd){
32104                 dd.lock();
32105             }
32106         }
32107         if(xy){
32108             el.avoidY = xy[1]-18;
32109             el.setXY(xy);
32110         }
32111         if(tm.animate){
32112             el.setOpacity(.1);
32113             el.setStyle("visibility", "visible");
32114             el.fadeIn({callback: afterShow});
32115         }else{
32116             afterShow();
32117         }
32118     };
32119     
32120     var afterShow = function(){
32121         if(ce){
32122             el.show();
32123             esc.enable();
32124             if(tm.autoDismiss && ce.autoHide !== false){
32125                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32126             }
32127         }
32128     };
32129     
32130     var hide = function(noanim){
32131         clearTimeout(dismissProc);
32132         clearTimeout(hideProc);
32133         ce = null;
32134         if(el.isVisible()){
32135             esc.disable();
32136             if(noanim !== true && tm.animate){
32137                 el.fadeOut({callback: afterHide});
32138             }else{
32139                 afterHide();
32140             } 
32141         }
32142     };
32143     
32144     var afterHide = function(){
32145         el.hide();
32146         if(removeCls){
32147             el.removeClass(removeCls);
32148             removeCls = null;
32149         }
32150     };
32151     
32152     return {
32153         /**
32154         * @cfg {Number} minWidth
32155         * The minimum width of the quick tip (defaults to 40)
32156         */
32157        minWidth : 40,
32158         /**
32159         * @cfg {Number} maxWidth
32160         * The maximum width of the quick tip (defaults to 300)
32161         */
32162        maxWidth : 300,
32163         /**
32164         * @cfg {Boolean} interceptTitles
32165         * True to automatically use the element's DOM title value if available (defaults to false)
32166         */
32167        interceptTitles : false,
32168         /**
32169         * @cfg {Boolean} trackMouse
32170         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32171         */
32172        trackMouse : false,
32173         /**
32174         * @cfg {Boolean} hideOnClick
32175         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32176         */
32177        hideOnClick : true,
32178         /**
32179         * @cfg {Number} showDelay
32180         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32181         */
32182        showDelay : 500,
32183         /**
32184         * @cfg {Number} hideDelay
32185         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32186         */
32187        hideDelay : 200,
32188         /**
32189         * @cfg {Boolean} autoHide
32190         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32191         * Used in conjunction with hideDelay.
32192         */
32193        autoHide : true,
32194         /**
32195         * @cfg {Boolean}
32196         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32197         * (defaults to true).  Used in conjunction with autoDismissDelay.
32198         */
32199        autoDismiss : true,
32200         /**
32201         * @cfg {Number}
32202         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32203         */
32204        autoDismissDelay : 5000,
32205        /**
32206         * @cfg {Boolean} animate
32207         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32208         */
32209        animate : false,
32210
32211        /**
32212         * @cfg {String} title
32213         * Title text to display (defaults to '').  This can be any valid HTML markup.
32214         */
32215         title: '',
32216        /**
32217         * @cfg {String} text
32218         * Body text to display (defaults to '').  This can be any valid HTML markup.
32219         */
32220         text : '',
32221        /**
32222         * @cfg {String} cls
32223         * A CSS class to apply to the base quick tip element (defaults to '').
32224         */
32225         cls : '',
32226        /**
32227         * @cfg {Number} width
32228         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32229         * minWidth or maxWidth.
32230         */
32231         width : null,
32232
32233     /**
32234      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32235      * or display QuickTips in a page.
32236      */
32237        init : function(){
32238           tm = Roo.QuickTips;
32239           cfg = tm.tagConfig;
32240           if(!inited){
32241               if(!Roo.isReady){ // allow calling of init() before onReady
32242                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32243                   return;
32244               }
32245               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32246               el.fxDefaults = {stopFx: true};
32247               // maximum custom styling
32248               //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>');
32249               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>');              
32250               tipTitle = el.child('h3');
32251               tipTitle.enableDisplayMode("block");
32252               tipBody = el.child('div.x-tip-bd');
32253               tipBodyText = el.child('div.x-tip-bd-inner');
32254               //bdLeft = el.child('div.x-tip-bd-left');
32255               //bdRight = el.child('div.x-tip-bd-right');
32256               close = el.child('div.x-tip-close');
32257               close.enableDisplayMode("block");
32258               close.on("click", hide);
32259               var d = Roo.get(document);
32260               d.on("mousedown", onDown);
32261               d.on("mouseover", onOver);
32262               d.on("mouseout", onOut);
32263               d.on("mousemove", onMove);
32264               esc = d.addKeyListener(27, hide);
32265               esc.disable();
32266               if(Roo.dd.DD){
32267                   dd = el.initDD("default", null, {
32268                       onDrag : function(){
32269                           el.sync();  
32270                       }
32271                   });
32272                   dd.setHandleElId(tipTitle.id);
32273                   dd.lock();
32274               }
32275               inited = true;
32276           }
32277           this.enable(); 
32278        },
32279
32280     /**
32281      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32282      * are supported:
32283      * <pre>
32284 Property    Type                   Description
32285 ----------  ---------------------  ------------------------------------------------------------------------
32286 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32287      * </ul>
32288      * @param {Object} config The config object
32289      */
32290        register : function(config){
32291            var cs = config instanceof Array ? config : arguments;
32292            for(var i = 0, len = cs.length; i < len; i++) {
32293                var c = cs[i];
32294                var target = c.target;
32295                if(target){
32296                    if(target instanceof Array){
32297                        for(var j = 0, jlen = target.length; j < jlen; j++){
32298                            tagEls[target[j]] = c;
32299                        }
32300                    }else{
32301                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32302                    }
32303                }
32304            }
32305        },
32306
32307     /**
32308      * Removes this quick tip from its element and destroys it.
32309      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32310      */
32311        unregister : function(el){
32312            delete tagEls[Roo.id(el)];
32313        },
32314
32315     /**
32316      * Enable this quick tip.
32317      */
32318        enable : function(){
32319            if(inited && disabled){
32320                locks.pop();
32321                if(locks.length < 1){
32322                    disabled = false;
32323                }
32324            }
32325        },
32326
32327     /**
32328      * Disable this quick tip.
32329      */
32330        disable : function(){
32331           disabled = true;
32332           clearTimeout(showProc);
32333           clearTimeout(hideProc);
32334           clearTimeout(dismissProc);
32335           if(ce){
32336               hide(true);
32337           }
32338           locks.push(1);
32339        },
32340
32341     /**
32342      * Returns true if the quick tip is enabled, else false.
32343      */
32344        isEnabled : function(){
32345             return !disabled;
32346        },
32347
32348         // private
32349        tagConfig : {
32350            namespace : "ext",
32351            attribute : "qtip",
32352            width : "width",
32353            target : "target",
32354            title : "qtitle",
32355            hide : "hide",
32356            cls : "qclass"
32357        }
32358    };
32359 }();
32360
32361 // backwards compat
32362 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32363  * Based on:
32364  * Ext JS Library 1.1.1
32365  * Copyright(c) 2006-2007, Ext JS, LLC.
32366  *
32367  * Originally Released Under LGPL - original licence link has changed is not relivant.
32368  *
32369  * Fork - LGPL
32370  * <script type="text/javascript">
32371  */
32372  
32373
32374 /**
32375  * @class Roo.tree.TreePanel
32376  * @extends Roo.data.Tree
32377
32378  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32379  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32380  * @cfg {Boolean} enableDD true to enable drag and drop
32381  * @cfg {Boolean} enableDrag true to enable just drag
32382  * @cfg {Boolean} enableDrop true to enable just drop
32383  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32384  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32385  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32386  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32387  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32388  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32389  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32390  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32391  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32392  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32393  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32394  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32395  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32396  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32397  * @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>
32398  * @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>
32399  * 
32400  * @constructor
32401  * @param {String/HTMLElement/Element} el The container element
32402  * @param {Object} config
32403  */
32404 Roo.tree.TreePanel = function(el, config){
32405     var root = false;
32406     var loader = false;
32407     if (config.root) {
32408         root = config.root;
32409         delete config.root;
32410     }
32411     if (config.loader) {
32412         loader = config.loader;
32413         delete config.loader;
32414     }
32415     
32416     Roo.apply(this, config);
32417     Roo.tree.TreePanel.superclass.constructor.call(this);
32418     this.el = Roo.get(el);
32419     this.el.addClass('x-tree');
32420     //console.log(root);
32421     if (root) {
32422         this.setRootNode( Roo.factory(root, Roo.tree));
32423     }
32424     if (loader) {
32425         this.loader = Roo.factory(loader, Roo.tree);
32426     }
32427    /**
32428     * Read-only. The id of the container element becomes this TreePanel's id.
32429     */
32430     this.id = this.el.id;
32431     this.addEvents({
32432         /**
32433         * @event beforeload
32434         * Fires before a node is loaded, return false to cancel
32435         * @param {Node} node The node being loaded
32436         */
32437         "beforeload" : true,
32438         /**
32439         * @event load
32440         * Fires when a node is loaded
32441         * @param {Node} node The node that was loaded
32442         */
32443         "load" : true,
32444         /**
32445         * @event textchange
32446         * Fires when the text for a node is changed
32447         * @param {Node} node The node
32448         * @param {String} text The new text
32449         * @param {String} oldText The old text
32450         */
32451         "textchange" : true,
32452         /**
32453         * @event beforeexpand
32454         * Fires before a node is expanded, return false to cancel.
32455         * @param {Node} node The node
32456         * @param {Boolean} deep
32457         * @param {Boolean} anim
32458         */
32459         "beforeexpand" : true,
32460         /**
32461         * @event beforecollapse
32462         * Fires before a node is collapsed, return false to cancel.
32463         * @param {Node} node The node
32464         * @param {Boolean} deep
32465         * @param {Boolean} anim
32466         */
32467         "beforecollapse" : true,
32468         /**
32469         * @event expand
32470         * Fires when a node is expanded
32471         * @param {Node} node The node
32472         */
32473         "expand" : true,
32474         /**
32475         * @event disabledchange
32476         * Fires when the disabled status of a node changes
32477         * @param {Node} node The node
32478         * @param {Boolean} disabled
32479         */
32480         "disabledchange" : true,
32481         /**
32482         * @event collapse
32483         * Fires when a node is collapsed
32484         * @param {Node} node The node
32485         */
32486         "collapse" : true,
32487         /**
32488         * @event beforeclick
32489         * Fires before click processing on a node. Return false to cancel the default action.
32490         * @param {Node} node The node
32491         * @param {Roo.EventObject} e The event object
32492         */
32493         "beforeclick":true,
32494         /**
32495         * @event checkchange
32496         * Fires when a node with a checkbox's checked property changes
32497         * @param {Node} this This node
32498         * @param {Boolean} checked
32499         */
32500         "checkchange":true,
32501         /**
32502         * @event click
32503         * Fires when a node is clicked
32504         * @param {Node} node The node
32505         * @param {Roo.EventObject} e The event object
32506         */
32507         "click":true,
32508         /**
32509         * @event dblclick
32510         * Fires when a node is double clicked
32511         * @param {Node} node The node
32512         * @param {Roo.EventObject} e The event object
32513         */
32514         "dblclick":true,
32515         /**
32516         * @event contextmenu
32517         * Fires when a node is right clicked
32518         * @param {Node} node The node
32519         * @param {Roo.EventObject} e The event object
32520         */
32521         "contextmenu":true,
32522         /**
32523         * @event beforechildrenrendered
32524         * Fires right before the child nodes for a node are rendered
32525         * @param {Node} node The node
32526         */
32527         "beforechildrenrendered":true,
32528         /**
32529         * @event startdrag
32530         * Fires when a node starts being dragged
32531         * @param {Roo.tree.TreePanel} this
32532         * @param {Roo.tree.TreeNode} node
32533         * @param {event} e The raw browser event
32534         */ 
32535        "startdrag" : true,
32536        /**
32537         * @event enddrag
32538         * Fires when a drag operation is complete
32539         * @param {Roo.tree.TreePanel} this
32540         * @param {Roo.tree.TreeNode} node
32541         * @param {event} e The raw browser event
32542         */
32543        "enddrag" : true,
32544        /**
32545         * @event dragdrop
32546         * Fires when a dragged node is dropped on a valid DD target
32547         * @param {Roo.tree.TreePanel} this
32548         * @param {Roo.tree.TreeNode} node
32549         * @param {DD} dd The dd it was dropped on
32550         * @param {event} e The raw browser event
32551         */
32552        "dragdrop" : true,
32553        /**
32554         * @event beforenodedrop
32555         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32556         * passed to handlers has the following properties:<br />
32557         * <ul style="padding:5px;padding-left:16px;">
32558         * <li>tree - The TreePanel</li>
32559         * <li>target - The node being targeted for the drop</li>
32560         * <li>data - The drag data from the drag source</li>
32561         * <li>point - The point of the drop - append, above or below</li>
32562         * <li>source - The drag source</li>
32563         * <li>rawEvent - Raw mouse event</li>
32564         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32565         * to be inserted by setting them on this object.</li>
32566         * <li>cancel - Set this to true to cancel the drop.</li>
32567         * </ul>
32568         * @param {Object} dropEvent
32569         */
32570        "beforenodedrop" : true,
32571        /**
32572         * @event nodedrop
32573         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32574         * passed to handlers has the following properties:<br />
32575         * <ul style="padding:5px;padding-left:16px;">
32576         * <li>tree - The TreePanel</li>
32577         * <li>target - The node being targeted for the drop</li>
32578         * <li>data - The drag data from the drag source</li>
32579         * <li>point - The point of the drop - append, above or below</li>
32580         * <li>source - The drag source</li>
32581         * <li>rawEvent - Raw mouse event</li>
32582         * <li>dropNode - Dropped node(s).</li>
32583         * </ul>
32584         * @param {Object} dropEvent
32585         */
32586        "nodedrop" : true,
32587         /**
32588         * @event nodedragover
32589         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32590         * passed to handlers has the following properties:<br />
32591         * <ul style="padding:5px;padding-left:16px;">
32592         * <li>tree - The TreePanel</li>
32593         * <li>target - The node being targeted for the drop</li>
32594         * <li>data - The drag data from the drag source</li>
32595         * <li>point - The point of the drop - append, above or below</li>
32596         * <li>source - The drag source</li>
32597         * <li>rawEvent - Raw mouse event</li>
32598         * <li>dropNode - Drop node(s) provided by the source.</li>
32599         * <li>cancel - Set this to true to signal drop not allowed.</li>
32600         * </ul>
32601         * @param {Object} dragOverEvent
32602         */
32603        "nodedragover" : true
32604         
32605     });
32606     if(this.singleExpand){
32607        this.on("beforeexpand", this.restrictExpand, this);
32608     }
32609     if (this.editor) {
32610         this.editor.tree = this;
32611         this.editor = Roo.factory(this.editor, Roo.tree);
32612     }
32613     
32614     if (this.selModel) {
32615         this.selModel = Roo.factory(this.selModel, Roo.tree);
32616     }
32617    
32618 };
32619 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32620     rootVisible : true,
32621     animate: Roo.enableFx,
32622     lines : true,
32623     enableDD : false,
32624     hlDrop : Roo.enableFx,
32625   
32626     renderer: false,
32627     
32628     rendererTip: false,
32629     // private
32630     restrictExpand : function(node){
32631         var p = node.parentNode;
32632         if(p){
32633             if(p.expandedChild && p.expandedChild.parentNode == p){
32634                 p.expandedChild.collapse();
32635             }
32636             p.expandedChild = node;
32637         }
32638     },
32639
32640     // private override
32641     setRootNode : function(node){
32642         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32643         if(!this.rootVisible){
32644             node.ui = new Roo.tree.RootTreeNodeUI(node);
32645         }
32646         return node;
32647     },
32648
32649     /**
32650      * Returns the container element for this TreePanel
32651      */
32652     getEl : function(){
32653         return this.el;
32654     },
32655
32656     /**
32657      * Returns the default TreeLoader for this TreePanel
32658      */
32659     getLoader : function(){
32660         return this.loader;
32661     },
32662
32663     /**
32664      * Expand all nodes
32665      */
32666     expandAll : function(){
32667         this.root.expand(true);
32668     },
32669
32670     /**
32671      * Collapse all nodes
32672      */
32673     collapseAll : function(){
32674         this.root.collapse(true);
32675     },
32676
32677     /**
32678      * Returns the selection model used by this TreePanel
32679      */
32680     getSelectionModel : function(){
32681         if(!this.selModel){
32682             this.selModel = new Roo.tree.DefaultSelectionModel();
32683         }
32684         return this.selModel;
32685     },
32686
32687     /**
32688      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32689      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32690      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32691      * @return {Array}
32692      */
32693     getChecked : function(a, startNode){
32694         startNode = startNode || this.root;
32695         var r = [];
32696         var f = function(){
32697             if(this.attributes.checked){
32698                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32699             }
32700         }
32701         startNode.cascade(f);
32702         return r;
32703     },
32704
32705     /**
32706      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32707      * @param {String} path
32708      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32709      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32710      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32711      */
32712     expandPath : function(path, attr, callback){
32713         attr = attr || "id";
32714         var keys = path.split(this.pathSeparator);
32715         var curNode = this.root;
32716         if(curNode.attributes[attr] != keys[1]){ // invalid root
32717             if(callback){
32718                 callback(false, null);
32719             }
32720             return;
32721         }
32722         var index = 1;
32723         var f = function(){
32724             if(++index == keys.length){
32725                 if(callback){
32726                     callback(true, curNode);
32727                 }
32728                 return;
32729             }
32730             var c = curNode.findChild(attr, keys[index]);
32731             if(!c){
32732                 if(callback){
32733                     callback(false, curNode);
32734                 }
32735                 return;
32736             }
32737             curNode = c;
32738             c.expand(false, false, f);
32739         };
32740         curNode.expand(false, false, f);
32741     },
32742
32743     /**
32744      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32745      * @param {String} path
32746      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32747      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32748      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32749      */
32750     selectPath : function(path, attr, callback){
32751         attr = attr || "id";
32752         var keys = path.split(this.pathSeparator);
32753         var v = keys.pop();
32754         if(keys.length > 0){
32755             var f = function(success, node){
32756                 if(success && node){
32757                     var n = node.findChild(attr, v);
32758                     if(n){
32759                         n.select();
32760                         if(callback){
32761                             callback(true, n);
32762                         }
32763                     }else if(callback){
32764                         callback(false, n);
32765                     }
32766                 }else{
32767                     if(callback){
32768                         callback(false, n);
32769                     }
32770                 }
32771             };
32772             this.expandPath(keys.join(this.pathSeparator), attr, f);
32773         }else{
32774             this.root.select();
32775             if(callback){
32776                 callback(true, this.root);
32777             }
32778         }
32779     },
32780
32781     getTreeEl : function(){
32782         return this.el;
32783     },
32784
32785     /**
32786      * Trigger rendering of this TreePanel
32787      */
32788     render : function(){
32789         if (this.innerCt) {
32790             return this; // stop it rendering more than once!!
32791         }
32792         
32793         this.innerCt = this.el.createChild({tag:"ul",
32794                cls:"x-tree-root-ct " +
32795                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32796
32797         if(this.containerScroll){
32798             Roo.dd.ScrollManager.register(this.el);
32799         }
32800         if((this.enableDD || this.enableDrop) && !this.dropZone){
32801            /**
32802             * The dropZone used by this tree if drop is enabled
32803             * @type Roo.tree.TreeDropZone
32804             */
32805              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32806                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32807            });
32808         }
32809         if((this.enableDD || this.enableDrag) && !this.dragZone){
32810            /**
32811             * The dragZone used by this tree if drag is enabled
32812             * @type Roo.tree.TreeDragZone
32813             */
32814             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32815                ddGroup: this.ddGroup || "TreeDD",
32816                scroll: this.ddScroll
32817            });
32818         }
32819         this.getSelectionModel().init(this);
32820         if (!this.root) {
32821             Roo.log("ROOT not set in tree");
32822             return this;
32823         }
32824         this.root.render();
32825         if(!this.rootVisible){
32826             this.root.renderChildren();
32827         }
32828         return this;
32829     }
32830 });/*
32831  * Based on:
32832  * Ext JS Library 1.1.1
32833  * Copyright(c) 2006-2007, Ext JS, LLC.
32834  *
32835  * Originally Released Under LGPL - original licence link has changed is not relivant.
32836  *
32837  * Fork - LGPL
32838  * <script type="text/javascript">
32839  */
32840  
32841
32842 /**
32843  * @class Roo.tree.DefaultSelectionModel
32844  * @extends Roo.util.Observable
32845  * The default single selection for a TreePanel.
32846  * @param {Object} cfg Configuration
32847  */
32848 Roo.tree.DefaultSelectionModel = function(cfg){
32849    this.selNode = null;
32850    
32851    
32852    
32853    this.addEvents({
32854        /**
32855         * @event selectionchange
32856         * Fires when the selected node changes
32857         * @param {DefaultSelectionModel} this
32858         * @param {TreeNode} node the new selection
32859         */
32860        "selectionchange" : true,
32861
32862        /**
32863         * @event beforeselect
32864         * Fires before the selected node changes, return false to cancel the change
32865         * @param {DefaultSelectionModel} this
32866         * @param {TreeNode} node the new selection
32867         * @param {TreeNode} node the old selection
32868         */
32869        "beforeselect" : true
32870    });
32871    
32872     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32873 };
32874
32875 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32876     init : function(tree){
32877         this.tree = tree;
32878         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32879         tree.on("click", this.onNodeClick, this);
32880     },
32881     
32882     onNodeClick : function(node, e){
32883         if (e.ctrlKey && this.selNode == node)  {
32884             this.unselect(node);
32885             return;
32886         }
32887         this.select(node);
32888     },
32889     
32890     /**
32891      * Select a node.
32892      * @param {TreeNode} node The node to select
32893      * @return {TreeNode} The selected node
32894      */
32895     select : function(node){
32896         var last = this.selNode;
32897         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32898             if(last){
32899                 last.ui.onSelectedChange(false);
32900             }
32901             this.selNode = node;
32902             node.ui.onSelectedChange(true);
32903             this.fireEvent("selectionchange", this, node, last);
32904         }
32905         return node;
32906     },
32907     
32908     /**
32909      * Deselect a node.
32910      * @param {TreeNode} node The node to unselect
32911      */
32912     unselect : function(node){
32913         if(this.selNode == node){
32914             this.clearSelections();
32915         }    
32916     },
32917     
32918     /**
32919      * Clear all selections
32920      */
32921     clearSelections : function(){
32922         var n = this.selNode;
32923         if(n){
32924             n.ui.onSelectedChange(false);
32925             this.selNode = null;
32926             this.fireEvent("selectionchange", this, null);
32927         }
32928         return n;
32929     },
32930     
32931     /**
32932      * Get the selected node
32933      * @return {TreeNode} The selected node
32934      */
32935     getSelectedNode : function(){
32936         return this.selNode;    
32937     },
32938     
32939     /**
32940      * Returns true if the node is selected
32941      * @param {TreeNode} node The node to check
32942      * @return {Boolean}
32943      */
32944     isSelected : function(node){
32945         return this.selNode == node;  
32946     },
32947
32948     /**
32949      * Selects the node above the selected node in the tree, intelligently walking the nodes
32950      * @return TreeNode The new selection
32951      */
32952     selectPrevious : function(){
32953         var s = this.selNode || this.lastSelNode;
32954         if(!s){
32955             return null;
32956         }
32957         var ps = s.previousSibling;
32958         if(ps){
32959             if(!ps.isExpanded() || ps.childNodes.length < 1){
32960                 return this.select(ps);
32961             } else{
32962                 var lc = ps.lastChild;
32963                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32964                     lc = lc.lastChild;
32965                 }
32966                 return this.select(lc);
32967             }
32968         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32969             return this.select(s.parentNode);
32970         }
32971         return null;
32972     },
32973
32974     /**
32975      * Selects the node above the selected node in the tree, intelligently walking the nodes
32976      * @return TreeNode The new selection
32977      */
32978     selectNext : function(){
32979         var s = this.selNode || this.lastSelNode;
32980         if(!s){
32981             return null;
32982         }
32983         if(s.firstChild && s.isExpanded()){
32984              return this.select(s.firstChild);
32985          }else if(s.nextSibling){
32986              return this.select(s.nextSibling);
32987          }else if(s.parentNode){
32988             var newS = null;
32989             s.parentNode.bubble(function(){
32990                 if(this.nextSibling){
32991                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32992                     return false;
32993                 }
32994             });
32995             return newS;
32996          }
32997         return null;
32998     },
32999
33000     onKeyDown : function(e){
33001         var s = this.selNode || this.lastSelNode;
33002         // undesirable, but required
33003         var sm = this;
33004         if(!s){
33005             return;
33006         }
33007         var k = e.getKey();
33008         switch(k){
33009              case e.DOWN:
33010                  e.stopEvent();
33011                  this.selectNext();
33012              break;
33013              case e.UP:
33014                  e.stopEvent();
33015                  this.selectPrevious();
33016              break;
33017              case e.RIGHT:
33018                  e.preventDefault();
33019                  if(s.hasChildNodes()){
33020                      if(!s.isExpanded()){
33021                          s.expand();
33022                      }else if(s.firstChild){
33023                          this.select(s.firstChild, e);
33024                      }
33025                  }
33026              break;
33027              case e.LEFT:
33028                  e.preventDefault();
33029                  if(s.hasChildNodes() && s.isExpanded()){
33030                      s.collapse();
33031                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33032                      this.select(s.parentNode, e);
33033                  }
33034              break;
33035         };
33036     }
33037 });
33038
33039 /**
33040  * @class Roo.tree.MultiSelectionModel
33041  * @extends Roo.util.Observable
33042  * Multi selection for a TreePanel.
33043  * @param {Object} cfg Configuration
33044  */
33045 Roo.tree.MultiSelectionModel = function(){
33046    this.selNodes = [];
33047    this.selMap = {};
33048    this.addEvents({
33049        /**
33050         * @event selectionchange
33051         * Fires when the selected nodes change
33052         * @param {MultiSelectionModel} this
33053         * @param {Array} nodes Array of the selected nodes
33054         */
33055        "selectionchange" : true
33056    });
33057    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33058    
33059 };
33060
33061 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33062     init : function(tree){
33063         this.tree = tree;
33064         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33065         tree.on("click", this.onNodeClick, this);
33066     },
33067     
33068     onNodeClick : function(node, e){
33069         this.select(node, e, e.ctrlKey);
33070     },
33071     
33072     /**
33073      * Select a node.
33074      * @param {TreeNode} node The node to select
33075      * @param {EventObject} e (optional) An event associated with the selection
33076      * @param {Boolean} keepExisting True to retain existing selections
33077      * @return {TreeNode} The selected node
33078      */
33079     select : function(node, e, keepExisting){
33080         if(keepExisting !== true){
33081             this.clearSelections(true);
33082         }
33083         if(this.isSelected(node)){
33084             this.lastSelNode = node;
33085             return node;
33086         }
33087         this.selNodes.push(node);
33088         this.selMap[node.id] = node;
33089         this.lastSelNode = node;
33090         node.ui.onSelectedChange(true);
33091         this.fireEvent("selectionchange", this, this.selNodes);
33092         return node;
33093     },
33094     
33095     /**
33096      * Deselect a node.
33097      * @param {TreeNode} node The node to unselect
33098      */
33099     unselect : function(node){
33100         if(this.selMap[node.id]){
33101             node.ui.onSelectedChange(false);
33102             var sn = this.selNodes;
33103             var index = -1;
33104             if(sn.indexOf){
33105                 index = sn.indexOf(node);
33106             }else{
33107                 for(var i = 0, len = sn.length; i < len; i++){
33108                     if(sn[i] == node){
33109                         index = i;
33110                         break;
33111                     }
33112                 }
33113             }
33114             if(index != -1){
33115                 this.selNodes.splice(index, 1);
33116             }
33117             delete this.selMap[node.id];
33118             this.fireEvent("selectionchange", this, this.selNodes);
33119         }
33120     },
33121     
33122     /**
33123      * Clear all selections
33124      */
33125     clearSelections : function(suppressEvent){
33126         var sn = this.selNodes;
33127         if(sn.length > 0){
33128             for(var i = 0, len = sn.length; i < len; i++){
33129                 sn[i].ui.onSelectedChange(false);
33130             }
33131             this.selNodes = [];
33132             this.selMap = {};
33133             if(suppressEvent !== true){
33134                 this.fireEvent("selectionchange", this, this.selNodes);
33135             }
33136         }
33137     },
33138     
33139     /**
33140      * Returns true if the node is selected
33141      * @param {TreeNode} node The node to check
33142      * @return {Boolean}
33143      */
33144     isSelected : function(node){
33145         return this.selMap[node.id] ? true : false;  
33146     },
33147     
33148     /**
33149      * Returns an array of the selected nodes
33150      * @return {Array}
33151      */
33152     getSelectedNodes : function(){
33153         return this.selNodes;    
33154     },
33155
33156     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33157
33158     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33159
33160     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33161 });/*
33162  * Based on:
33163  * Ext JS Library 1.1.1
33164  * Copyright(c) 2006-2007, Ext JS, LLC.
33165  *
33166  * Originally Released Under LGPL - original licence link has changed is not relivant.
33167  *
33168  * Fork - LGPL
33169  * <script type="text/javascript">
33170  */
33171  
33172 /**
33173  * @class Roo.tree.TreeNode
33174  * @extends Roo.data.Node
33175  * @cfg {String} text The text for this node
33176  * @cfg {Boolean} expanded true to start the node expanded
33177  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33178  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33179  * @cfg {Boolean} disabled true to start the node disabled
33180  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33181  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33182  * @cfg {String} cls A css class to be added to the node
33183  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33184  * @cfg {String} href URL of the link used for the node (defaults to #)
33185  * @cfg {String} hrefTarget target frame for the link
33186  * @cfg {String} qtip An Ext QuickTip for the node
33187  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33188  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33189  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33190  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33191  * (defaults to undefined with no checkbox rendered)
33192  * @constructor
33193  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33194  */
33195 Roo.tree.TreeNode = function(attributes){
33196     attributes = attributes || {};
33197     if(typeof attributes == "string"){
33198         attributes = {text: attributes};
33199     }
33200     this.childrenRendered = false;
33201     this.rendered = false;
33202     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33203     this.expanded = attributes.expanded === true;
33204     this.isTarget = attributes.isTarget !== false;
33205     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33206     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33207
33208     /**
33209      * Read-only. The text for this node. To change it use setText().
33210      * @type String
33211      */
33212     this.text = attributes.text;
33213     /**
33214      * True if this node is disabled.
33215      * @type Boolean
33216      */
33217     this.disabled = attributes.disabled === true;
33218
33219     this.addEvents({
33220         /**
33221         * @event textchange
33222         * Fires when the text for this node is changed
33223         * @param {Node} this This node
33224         * @param {String} text The new text
33225         * @param {String} oldText The old text
33226         */
33227         "textchange" : true,
33228         /**
33229         * @event beforeexpand
33230         * Fires before this node is expanded, return false to cancel.
33231         * @param {Node} this This node
33232         * @param {Boolean} deep
33233         * @param {Boolean} anim
33234         */
33235         "beforeexpand" : true,
33236         /**
33237         * @event beforecollapse
33238         * Fires before this node is collapsed, return false to cancel.
33239         * @param {Node} this This node
33240         * @param {Boolean} deep
33241         * @param {Boolean} anim
33242         */
33243         "beforecollapse" : true,
33244         /**
33245         * @event expand
33246         * Fires when this node is expanded
33247         * @param {Node} this This node
33248         */
33249         "expand" : true,
33250         /**
33251         * @event disabledchange
33252         * Fires when the disabled status of this node changes
33253         * @param {Node} this This node
33254         * @param {Boolean} disabled
33255         */
33256         "disabledchange" : true,
33257         /**
33258         * @event collapse
33259         * Fires when this node is collapsed
33260         * @param {Node} this This node
33261         */
33262         "collapse" : true,
33263         /**
33264         * @event beforeclick
33265         * Fires before click processing. Return false to cancel the default action.
33266         * @param {Node} this This node
33267         * @param {Roo.EventObject} e The event object
33268         */
33269         "beforeclick":true,
33270         /**
33271         * @event checkchange
33272         * Fires when a node with a checkbox's checked property changes
33273         * @param {Node} this This node
33274         * @param {Boolean} checked
33275         */
33276         "checkchange":true,
33277         /**
33278         * @event click
33279         * Fires when this node is clicked
33280         * @param {Node} this This node
33281         * @param {Roo.EventObject} e The event object
33282         */
33283         "click":true,
33284         /**
33285         * @event dblclick
33286         * Fires when this node is double clicked
33287         * @param {Node} this This node
33288         * @param {Roo.EventObject} e The event object
33289         */
33290         "dblclick":true,
33291         /**
33292         * @event contextmenu
33293         * Fires when this node is right clicked
33294         * @param {Node} this This node
33295         * @param {Roo.EventObject} e The event object
33296         */
33297         "contextmenu":true,
33298         /**
33299         * @event beforechildrenrendered
33300         * Fires right before the child nodes for this node are rendered
33301         * @param {Node} this This node
33302         */
33303         "beforechildrenrendered":true
33304     });
33305
33306     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33307
33308     /**
33309      * Read-only. The UI for this node
33310      * @type TreeNodeUI
33311      */
33312     this.ui = new uiClass(this);
33313     
33314     // finally support items[]
33315     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33316         return;
33317     }
33318     
33319     
33320     Roo.each(this.attributes.items, function(c) {
33321         this.appendChild(Roo.factory(c,Roo.Tree));
33322     }, this);
33323     delete this.attributes.items;
33324     
33325     
33326     
33327 };
33328 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33329     preventHScroll: true,
33330     /**
33331      * Returns true if this node is expanded
33332      * @return {Boolean}
33333      */
33334     isExpanded : function(){
33335         return this.expanded;
33336     },
33337
33338     /**
33339      * Returns the UI object for this node
33340      * @return {TreeNodeUI}
33341      */
33342     getUI : function(){
33343         return this.ui;
33344     },
33345
33346     // private override
33347     setFirstChild : function(node){
33348         var of = this.firstChild;
33349         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33350         if(this.childrenRendered && of && node != of){
33351             of.renderIndent(true, true);
33352         }
33353         if(this.rendered){
33354             this.renderIndent(true, true);
33355         }
33356     },
33357
33358     // private override
33359     setLastChild : function(node){
33360         var ol = this.lastChild;
33361         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33362         if(this.childrenRendered && ol && node != ol){
33363             ol.renderIndent(true, true);
33364         }
33365         if(this.rendered){
33366             this.renderIndent(true, true);
33367         }
33368     },
33369
33370     // these methods are overridden to provide lazy rendering support
33371     // private override
33372     appendChild : function()
33373     {
33374         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33375         if(node && this.childrenRendered){
33376             node.render();
33377         }
33378         this.ui.updateExpandIcon();
33379         return node;
33380     },
33381
33382     // private override
33383     removeChild : function(node){
33384         this.ownerTree.getSelectionModel().unselect(node);
33385         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33386         // if it's been rendered remove dom node
33387         if(this.childrenRendered){
33388             node.ui.remove();
33389         }
33390         if(this.childNodes.length < 1){
33391             this.collapse(false, false);
33392         }else{
33393             this.ui.updateExpandIcon();
33394         }
33395         if(!this.firstChild) {
33396             this.childrenRendered = false;
33397         }
33398         return node;
33399     },
33400
33401     // private override
33402     insertBefore : function(node, refNode){
33403         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33404         if(newNode && refNode && this.childrenRendered){
33405             node.render();
33406         }
33407         this.ui.updateExpandIcon();
33408         return newNode;
33409     },
33410
33411     /**
33412      * Sets the text for this node
33413      * @param {String} text
33414      */
33415     setText : function(text){
33416         var oldText = this.text;
33417         this.text = text;
33418         this.attributes.text = text;
33419         if(this.rendered){ // event without subscribing
33420             this.ui.onTextChange(this, text, oldText);
33421         }
33422         this.fireEvent("textchange", this, text, oldText);
33423     },
33424
33425     /**
33426      * Triggers selection of this node
33427      */
33428     select : function(){
33429         this.getOwnerTree().getSelectionModel().select(this);
33430     },
33431
33432     /**
33433      * Triggers deselection of this node
33434      */
33435     unselect : function(){
33436         this.getOwnerTree().getSelectionModel().unselect(this);
33437     },
33438
33439     /**
33440      * Returns true if this node is selected
33441      * @return {Boolean}
33442      */
33443     isSelected : function(){
33444         return this.getOwnerTree().getSelectionModel().isSelected(this);
33445     },
33446
33447     /**
33448      * Expand this node.
33449      * @param {Boolean} deep (optional) True to expand all children as well
33450      * @param {Boolean} anim (optional) false to cancel the default animation
33451      * @param {Function} callback (optional) A callback to be called when
33452      * expanding this node completes (does not wait for deep expand to complete).
33453      * Called with 1 parameter, this node.
33454      */
33455     expand : function(deep, anim, callback){
33456         if(!this.expanded){
33457             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33458                 return;
33459             }
33460             if(!this.childrenRendered){
33461                 this.renderChildren();
33462             }
33463             this.expanded = true;
33464             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33465                 this.ui.animExpand(function(){
33466                     this.fireEvent("expand", this);
33467                     if(typeof callback == "function"){
33468                         callback(this);
33469                     }
33470                     if(deep === true){
33471                         this.expandChildNodes(true);
33472                     }
33473                 }.createDelegate(this));
33474                 return;
33475             }else{
33476                 this.ui.expand();
33477                 this.fireEvent("expand", this);
33478                 if(typeof callback == "function"){
33479                     callback(this);
33480                 }
33481             }
33482         }else{
33483            if(typeof callback == "function"){
33484                callback(this);
33485            }
33486         }
33487         if(deep === true){
33488             this.expandChildNodes(true);
33489         }
33490     },
33491
33492     isHiddenRoot : function(){
33493         return this.isRoot && !this.getOwnerTree().rootVisible;
33494     },
33495
33496     /**
33497      * Collapse this node.
33498      * @param {Boolean} deep (optional) True to collapse all children as well
33499      * @param {Boolean} anim (optional) false to cancel the default animation
33500      */
33501     collapse : function(deep, anim){
33502         if(this.expanded && !this.isHiddenRoot()){
33503             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33504                 return;
33505             }
33506             this.expanded = false;
33507             if((this.getOwnerTree().animate && anim !== false) || anim){
33508                 this.ui.animCollapse(function(){
33509                     this.fireEvent("collapse", this);
33510                     if(deep === true){
33511                         this.collapseChildNodes(true);
33512                     }
33513                 }.createDelegate(this));
33514                 return;
33515             }else{
33516                 this.ui.collapse();
33517                 this.fireEvent("collapse", this);
33518             }
33519         }
33520         if(deep === true){
33521             var cs = this.childNodes;
33522             for(var i = 0, len = cs.length; i < len; i++) {
33523                 cs[i].collapse(true, false);
33524             }
33525         }
33526     },
33527
33528     // private
33529     delayedExpand : function(delay){
33530         if(!this.expandProcId){
33531             this.expandProcId = this.expand.defer(delay, this);
33532         }
33533     },
33534
33535     // private
33536     cancelExpand : function(){
33537         if(this.expandProcId){
33538             clearTimeout(this.expandProcId);
33539         }
33540         this.expandProcId = false;
33541     },
33542
33543     /**
33544      * Toggles expanded/collapsed state of the node
33545      */
33546     toggle : function(){
33547         if(this.expanded){
33548             this.collapse();
33549         }else{
33550             this.expand();
33551         }
33552     },
33553
33554     /**
33555      * Ensures all parent nodes are expanded
33556      */
33557     ensureVisible : function(callback){
33558         var tree = this.getOwnerTree();
33559         tree.expandPath(this.parentNode.getPath(), false, function(){
33560             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33561             Roo.callback(callback);
33562         }.createDelegate(this));
33563     },
33564
33565     /**
33566      * Expand all child nodes
33567      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33568      */
33569     expandChildNodes : function(deep){
33570         var cs = this.childNodes;
33571         for(var i = 0, len = cs.length; i < len; i++) {
33572                 cs[i].expand(deep);
33573         }
33574     },
33575
33576     /**
33577      * Collapse all child nodes
33578      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33579      */
33580     collapseChildNodes : function(deep){
33581         var cs = this.childNodes;
33582         for(var i = 0, len = cs.length; i < len; i++) {
33583                 cs[i].collapse(deep);
33584         }
33585     },
33586
33587     /**
33588      * Disables this node
33589      */
33590     disable : function(){
33591         this.disabled = true;
33592         this.unselect();
33593         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33594             this.ui.onDisableChange(this, true);
33595         }
33596         this.fireEvent("disabledchange", this, true);
33597     },
33598
33599     /**
33600      * Enables this node
33601      */
33602     enable : function(){
33603         this.disabled = false;
33604         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33605             this.ui.onDisableChange(this, false);
33606         }
33607         this.fireEvent("disabledchange", this, false);
33608     },
33609
33610     // private
33611     renderChildren : function(suppressEvent){
33612         if(suppressEvent !== false){
33613             this.fireEvent("beforechildrenrendered", this);
33614         }
33615         var cs = this.childNodes;
33616         for(var i = 0, len = cs.length; i < len; i++){
33617             cs[i].render(true);
33618         }
33619         this.childrenRendered = true;
33620     },
33621
33622     // private
33623     sort : function(fn, scope){
33624         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33625         if(this.childrenRendered){
33626             var cs = this.childNodes;
33627             for(var i = 0, len = cs.length; i < len; i++){
33628                 cs[i].render(true);
33629             }
33630         }
33631     },
33632
33633     // private
33634     render : function(bulkRender){
33635         this.ui.render(bulkRender);
33636         if(!this.rendered){
33637             this.rendered = true;
33638             if(this.expanded){
33639                 this.expanded = false;
33640                 this.expand(false, false);
33641             }
33642         }
33643     },
33644
33645     // private
33646     renderIndent : function(deep, refresh){
33647         if(refresh){
33648             this.ui.childIndent = null;
33649         }
33650         this.ui.renderIndent();
33651         if(deep === true && this.childrenRendered){
33652             var cs = this.childNodes;
33653             for(var i = 0, len = cs.length; i < len; i++){
33654                 cs[i].renderIndent(true, refresh);
33655             }
33656         }
33657     }
33658 });/*
33659  * Based on:
33660  * Ext JS Library 1.1.1
33661  * Copyright(c) 2006-2007, Ext JS, LLC.
33662  *
33663  * Originally Released Under LGPL - original licence link has changed is not relivant.
33664  *
33665  * Fork - LGPL
33666  * <script type="text/javascript">
33667  */
33668  
33669 /**
33670  * @class Roo.tree.AsyncTreeNode
33671  * @extends Roo.tree.TreeNode
33672  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33673  * @constructor
33674  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33675  */
33676  Roo.tree.AsyncTreeNode = function(config){
33677     this.loaded = false;
33678     this.loading = false;
33679     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33680     /**
33681     * @event beforeload
33682     * Fires before this node is loaded, return false to cancel
33683     * @param {Node} this This node
33684     */
33685     this.addEvents({'beforeload':true, 'load': true});
33686     /**
33687     * @event load
33688     * Fires when this node is loaded
33689     * @param {Node} this This node
33690     */
33691     /**
33692      * The loader used by this node (defaults to using the tree's defined loader)
33693      * @type TreeLoader
33694      * @property loader
33695      */
33696 };
33697 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33698     expand : function(deep, anim, callback){
33699         if(this.loading){ // if an async load is already running, waiting til it's done
33700             var timer;
33701             var f = function(){
33702                 if(!this.loading){ // done loading
33703                     clearInterval(timer);
33704                     this.expand(deep, anim, callback);
33705                 }
33706             }.createDelegate(this);
33707             timer = setInterval(f, 200);
33708             return;
33709         }
33710         if(!this.loaded){
33711             if(this.fireEvent("beforeload", this) === false){
33712                 return;
33713             }
33714             this.loading = true;
33715             this.ui.beforeLoad(this);
33716             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33717             if(loader){
33718                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33719                 return;
33720             }
33721         }
33722         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33723     },
33724     
33725     /**
33726      * Returns true if this node is currently loading
33727      * @return {Boolean}
33728      */
33729     isLoading : function(){
33730         return this.loading;  
33731     },
33732     
33733     loadComplete : function(deep, anim, callback){
33734         this.loading = false;
33735         this.loaded = true;
33736         this.ui.afterLoad(this);
33737         this.fireEvent("load", this);
33738         this.expand(deep, anim, callback);
33739     },
33740     
33741     /**
33742      * Returns true if this node has been loaded
33743      * @return {Boolean}
33744      */
33745     isLoaded : function(){
33746         return this.loaded;
33747     },
33748     
33749     hasChildNodes : function(){
33750         if(!this.isLeaf() && !this.loaded){
33751             return true;
33752         }else{
33753             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33754         }
33755     },
33756
33757     /**
33758      * Trigger a reload for this node
33759      * @param {Function} callback
33760      */
33761     reload : function(callback){
33762         this.collapse(false, false);
33763         while(this.firstChild){
33764             this.removeChild(this.firstChild);
33765         }
33766         this.childrenRendered = false;
33767         this.loaded = false;
33768         if(this.isHiddenRoot()){
33769             this.expanded = false;
33770         }
33771         this.expand(false, false, callback);
33772     }
33773 });/*
33774  * Based on:
33775  * Ext JS Library 1.1.1
33776  * Copyright(c) 2006-2007, Ext JS, LLC.
33777  *
33778  * Originally Released Under LGPL - original licence link has changed is not relivant.
33779  *
33780  * Fork - LGPL
33781  * <script type="text/javascript">
33782  */
33783  
33784 /**
33785  * @class Roo.tree.TreeNodeUI
33786  * @constructor
33787  * @param {Object} node The node to render
33788  * The TreeNode UI implementation is separate from the
33789  * tree implementation. Unless you are customizing the tree UI,
33790  * you should never have to use this directly.
33791  */
33792 Roo.tree.TreeNodeUI = function(node){
33793     this.node = node;
33794     this.rendered = false;
33795     this.animating = false;
33796     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33797 };
33798
33799 Roo.tree.TreeNodeUI.prototype = {
33800     removeChild : function(node){
33801         if(this.rendered){
33802             this.ctNode.removeChild(node.ui.getEl());
33803         }
33804     },
33805
33806     beforeLoad : function(){
33807          this.addClass("x-tree-node-loading");
33808     },
33809
33810     afterLoad : function(){
33811          this.removeClass("x-tree-node-loading");
33812     },
33813
33814     onTextChange : function(node, text, oldText){
33815         if(this.rendered){
33816             this.textNode.innerHTML = text;
33817         }
33818     },
33819
33820     onDisableChange : function(node, state){
33821         this.disabled = state;
33822         if(state){
33823             this.addClass("x-tree-node-disabled");
33824         }else{
33825             this.removeClass("x-tree-node-disabled");
33826         }
33827     },
33828
33829     onSelectedChange : function(state){
33830         if(state){
33831             this.focus();
33832             this.addClass("x-tree-selected");
33833         }else{
33834             //this.blur();
33835             this.removeClass("x-tree-selected");
33836         }
33837     },
33838
33839     onMove : function(tree, node, oldParent, newParent, index, refNode){
33840         this.childIndent = null;
33841         if(this.rendered){
33842             var targetNode = newParent.ui.getContainer();
33843             if(!targetNode){//target not rendered
33844                 this.holder = document.createElement("div");
33845                 this.holder.appendChild(this.wrap);
33846                 return;
33847             }
33848             var insertBefore = refNode ? refNode.ui.getEl() : null;
33849             if(insertBefore){
33850                 targetNode.insertBefore(this.wrap, insertBefore);
33851             }else{
33852                 targetNode.appendChild(this.wrap);
33853             }
33854             this.node.renderIndent(true);
33855         }
33856     },
33857
33858     addClass : function(cls){
33859         if(this.elNode){
33860             Roo.fly(this.elNode).addClass(cls);
33861         }
33862     },
33863
33864     removeClass : function(cls){
33865         if(this.elNode){
33866             Roo.fly(this.elNode).removeClass(cls);
33867         }
33868     },
33869
33870     remove : function(){
33871         if(this.rendered){
33872             this.holder = document.createElement("div");
33873             this.holder.appendChild(this.wrap);
33874         }
33875     },
33876
33877     fireEvent : function(){
33878         return this.node.fireEvent.apply(this.node, arguments);
33879     },
33880
33881     initEvents : function(){
33882         this.node.on("move", this.onMove, this);
33883         var E = Roo.EventManager;
33884         var a = this.anchor;
33885
33886         var el = Roo.fly(a, '_treeui');
33887
33888         if(Roo.isOpera){ // opera render bug ignores the CSS
33889             el.setStyle("text-decoration", "none");
33890         }
33891
33892         el.on("click", this.onClick, this);
33893         el.on("dblclick", this.onDblClick, this);
33894
33895         if(this.checkbox){
33896             Roo.EventManager.on(this.checkbox,
33897                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33898         }
33899
33900         el.on("contextmenu", this.onContextMenu, this);
33901
33902         var icon = Roo.fly(this.iconNode);
33903         icon.on("click", this.onClick, this);
33904         icon.on("dblclick", this.onDblClick, this);
33905         icon.on("contextmenu", this.onContextMenu, this);
33906         E.on(this.ecNode, "click", this.ecClick, this, true);
33907
33908         if(this.node.disabled){
33909             this.addClass("x-tree-node-disabled");
33910         }
33911         if(this.node.hidden){
33912             this.addClass("x-tree-node-disabled");
33913         }
33914         var ot = this.node.getOwnerTree();
33915         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33916         if(dd && (!this.node.isRoot || ot.rootVisible)){
33917             Roo.dd.Registry.register(this.elNode, {
33918                 node: this.node,
33919                 handles: this.getDDHandles(),
33920                 isHandle: false
33921             });
33922         }
33923     },
33924
33925     getDDHandles : function(){
33926         return [this.iconNode, this.textNode];
33927     },
33928
33929     hide : function(){
33930         if(this.rendered){
33931             this.wrap.style.display = "none";
33932         }
33933     },
33934
33935     show : function(){
33936         if(this.rendered){
33937             this.wrap.style.display = "";
33938         }
33939     },
33940
33941     onContextMenu : function(e){
33942         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33943             e.preventDefault();
33944             this.focus();
33945             this.fireEvent("contextmenu", this.node, e);
33946         }
33947     },
33948
33949     onClick : function(e){
33950         if(this.dropping){
33951             e.stopEvent();
33952             return;
33953         }
33954         if(this.fireEvent("beforeclick", this.node, e) !== false){
33955             if(!this.disabled && this.node.attributes.href){
33956                 this.fireEvent("click", this.node, e);
33957                 return;
33958             }
33959             e.preventDefault();
33960             if(this.disabled){
33961                 return;
33962             }
33963
33964             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33965                 this.node.toggle();
33966             }
33967
33968             this.fireEvent("click", this.node, e);
33969         }else{
33970             e.stopEvent();
33971         }
33972     },
33973
33974     onDblClick : function(e){
33975         e.preventDefault();
33976         if(this.disabled){
33977             return;
33978         }
33979         if(this.checkbox){
33980             this.toggleCheck();
33981         }
33982         if(!this.animating && this.node.hasChildNodes()){
33983             this.node.toggle();
33984         }
33985         this.fireEvent("dblclick", this.node, e);
33986     },
33987
33988     onCheckChange : function(){
33989         var checked = this.checkbox.checked;
33990         this.node.attributes.checked = checked;
33991         this.fireEvent('checkchange', this.node, checked);
33992     },
33993
33994     ecClick : function(e){
33995         if(!this.animating && this.node.hasChildNodes()){
33996             this.node.toggle();
33997         }
33998     },
33999
34000     startDrop : function(){
34001         this.dropping = true;
34002     },
34003
34004     // delayed drop so the click event doesn't get fired on a drop
34005     endDrop : function(){
34006        setTimeout(function(){
34007            this.dropping = false;
34008        }.createDelegate(this), 50);
34009     },
34010
34011     expand : function(){
34012         this.updateExpandIcon();
34013         this.ctNode.style.display = "";
34014     },
34015
34016     focus : function(){
34017         if(!this.node.preventHScroll){
34018             try{this.anchor.focus();
34019             }catch(e){}
34020         }else if(!Roo.isIE){
34021             try{
34022                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34023                 var l = noscroll.scrollLeft;
34024                 this.anchor.focus();
34025                 noscroll.scrollLeft = l;
34026             }catch(e){}
34027         }
34028     },
34029
34030     toggleCheck : function(value){
34031         var cb = this.checkbox;
34032         if(cb){
34033             cb.checked = (value === undefined ? !cb.checked : value);
34034         }
34035     },
34036
34037     blur : function(){
34038         try{
34039             this.anchor.blur();
34040         }catch(e){}
34041     },
34042
34043     animExpand : function(callback){
34044         var ct = Roo.get(this.ctNode);
34045         ct.stopFx();
34046         if(!this.node.hasChildNodes()){
34047             this.updateExpandIcon();
34048             this.ctNode.style.display = "";
34049             Roo.callback(callback);
34050             return;
34051         }
34052         this.animating = true;
34053         this.updateExpandIcon();
34054
34055         ct.slideIn('t', {
34056            callback : function(){
34057                this.animating = false;
34058                Roo.callback(callback);
34059             },
34060             scope: this,
34061             duration: this.node.ownerTree.duration || .25
34062         });
34063     },
34064
34065     highlight : function(){
34066         var tree = this.node.getOwnerTree();
34067         Roo.fly(this.wrap).highlight(
34068             tree.hlColor || "C3DAF9",
34069             {endColor: tree.hlBaseColor}
34070         );
34071     },
34072
34073     collapse : function(){
34074         this.updateExpandIcon();
34075         this.ctNode.style.display = "none";
34076     },
34077
34078     animCollapse : function(callback){
34079         var ct = Roo.get(this.ctNode);
34080         ct.enableDisplayMode('block');
34081         ct.stopFx();
34082
34083         this.animating = true;
34084         this.updateExpandIcon();
34085
34086         ct.slideOut('t', {
34087             callback : function(){
34088                this.animating = false;
34089                Roo.callback(callback);
34090             },
34091             scope: this,
34092             duration: this.node.ownerTree.duration || .25
34093         });
34094     },
34095
34096     getContainer : function(){
34097         return this.ctNode;
34098     },
34099
34100     getEl : function(){
34101         return this.wrap;
34102     },
34103
34104     appendDDGhost : function(ghostNode){
34105         ghostNode.appendChild(this.elNode.cloneNode(true));
34106     },
34107
34108     getDDRepairXY : function(){
34109         return Roo.lib.Dom.getXY(this.iconNode);
34110     },
34111
34112     onRender : function(){
34113         this.render();
34114     },
34115
34116     render : function(bulkRender){
34117         var n = this.node, a = n.attributes;
34118         var targetNode = n.parentNode ?
34119               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34120
34121         if(!this.rendered){
34122             this.rendered = true;
34123
34124             this.renderElements(n, a, targetNode, bulkRender);
34125
34126             if(a.qtip){
34127                if(this.textNode.setAttributeNS){
34128                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34129                    if(a.qtipTitle){
34130                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34131                    }
34132                }else{
34133                    this.textNode.setAttribute("ext:qtip", a.qtip);
34134                    if(a.qtipTitle){
34135                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34136                    }
34137                }
34138             }else if(a.qtipCfg){
34139                 a.qtipCfg.target = Roo.id(this.textNode);
34140                 Roo.QuickTips.register(a.qtipCfg);
34141             }
34142             this.initEvents();
34143             if(!this.node.expanded){
34144                 this.updateExpandIcon();
34145             }
34146         }else{
34147             if(bulkRender === true) {
34148                 targetNode.appendChild(this.wrap);
34149             }
34150         }
34151     },
34152
34153     renderElements : function(n, a, targetNode, bulkRender)
34154     {
34155         // add some indent caching, this helps performance when rendering a large tree
34156         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34157         var t = n.getOwnerTree();
34158         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34159         if (typeof(n.attributes.html) != 'undefined') {
34160             txt = n.attributes.html;
34161         }
34162         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34163         var cb = typeof a.checked == 'boolean';
34164         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34165         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34166             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34167             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34168             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34169             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34170             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34171              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34172                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34173             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34174             "</li>"];
34175
34176         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34177             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34178                                 n.nextSibling.ui.getEl(), buf.join(""));
34179         }else{
34180             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34181         }
34182
34183         this.elNode = this.wrap.childNodes[0];
34184         this.ctNode = this.wrap.childNodes[1];
34185         var cs = this.elNode.childNodes;
34186         this.indentNode = cs[0];
34187         this.ecNode = cs[1];
34188         this.iconNode = cs[2];
34189         var index = 3;
34190         if(cb){
34191             this.checkbox = cs[3];
34192             index++;
34193         }
34194         this.anchor = cs[index];
34195         this.textNode = cs[index].firstChild;
34196     },
34197
34198     getAnchor : function(){
34199         return this.anchor;
34200     },
34201
34202     getTextEl : function(){
34203         return this.textNode;
34204     },
34205
34206     getIconEl : function(){
34207         return this.iconNode;
34208     },
34209
34210     isChecked : function(){
34211         return this.checkbox ? this.checkbox.checked : false;
34212     },
34213
34214     updateExpandIcon : function(){
34215         if(this.rendered){
34216             var n = this.node, c1, c2;
34217             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34218             var hasChild = n.hasChildNodes();
34219             if(hasChild){
34220                 if(n.expanded){
34221                     cls += "-minus";
34222                     c1 = "x-tree-node-collapsed";
34223                     c2 = "x-tree-node-expanded";
34224                 }else{
34225                     cls += "-plus";
34226                     c1 = "x-tree-node-expanded";
34227                     c2 = "x-tree-node-collapsed";
34228                 }
34229                 if(this.wasLeaf){
34230                     this.removeClass("x-tree-node-leaf");
34231                     this.wasLeaf = false;
34232                 }
34233                 if(this.c1 != c1 || this.c2 != c2){
34234                     Roo.fly(this.elNode).replaceClass(c1, c2);
34235                     this.c1 = c1; this.c2 = c2;
34236                 }
34237             }else{
34238                 // this changes non-leafs into leafs if they have no children.
34239                 // it's not very rational behaviour..
34240                 
34241                 if(!this.wasLeaf && this.node.leaf){
34242                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34243                     delete this.c1;
34244                     delete this.c2;
34245                     this.wasLeaf = true;
34246                 }
34247             }
34248             var ecc = "x-tree-ec-icon "+cls;
34249             if(this.ecc != ecc){
34250                 this.ecNode.className = ecc;
34251                 this.ecc = ecc;
34252             }
34253         }
34254     },
34255
34256     getChildIndent : function(){
34257         if(!this.childIndent){
34258             var buf = [];
34259             var p = this.node;
34260             while(p){
34261                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34262                     if(!p.isLast()) {
34263                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34264                     } else {
34265                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34266                     }
34267                 }
34268                 p = p.parentNode;
34269             }
34270             this.childIndent = buf.join("");
34271         }
34272         return this.childIndent;
34273     },
34274
34275     renderIndent : function(){
34276         if(this.rendered){
34277             var indent = "";
34278             var p = this.node.parentNode;
34279             if(p){
34280                 indent = p.ui.getChildIndent();
34281             }
34282             if(this.indentMarkup != indent){ // don't rerender if not required
34283                 this.indentNode.innerHTML = indent;
34284                 this.indentMarkup = indent;
34285             }
34286             this.updateExpandIcon();
34287         }
34288     }
34289 };
34290
34291 Roo.tree.RootTreeNodeUI = function(){
34292     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34293 };
34294 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34295     render : function(){
34296         if(!this.rendered){
34297             var targetNode = this.node.ownerTree.innerCt.dom;
34298             this.node.expanded = true;
34299             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34300             this.wrap = this.ctNode = targetNode.firstChild;
34301         }
34302     },
34303     collapse : function(){
34304     },
34305     expand : function(){
34306     }
34307 });/*
34308  * Based on:
34309  * Ext JS Library 1.1.1
34310  * Copyright(c) 2006-2007, Ext JS, LLC.
34311  *
34312  * Originally Released Under LGPL - original licence link has changed is not relivant.
34313  *
34314  * Fork - LGPL
34315  * <script type="text/javascript">
34316  */
34317 /**
34318  * @class Roo.tree.TreeLoader
34319  * @extends Roo.util.Observable
34320  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34321  * nodes from a specified URL. The response must be a javascript Array definition
34322  * who's elements are node definition objects. eg:
34323  * <pre><code>
34324 {  success : true,
34325    data :      [
34326    
34327     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34328     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34329     ]
34330 }
34331
34332
34333 </code></pre>
34334  * <br><br>
34335  * The old style respose with just an array is still supported, but not recommended.
34336  * <br><br>
34337  *
34338  * A server request is sent, and child nodes are loaded only when a node is expanded.
34339  * The loading node's id is passed to the server under the parameter name "node" to
34340  * enable the server to produce the correct child nodes.
34341  * <br><br>
34342  * To pass extra parameters, an event handler may be attached to the "beforeload"
34343  * event, and the parameters specified in the TreeLoader's baseParams property:
34344  * <pre><code>
34345     myTreeLoader.on("beforeload", function(treeLoader, node) {
34346         this.baseParams.category = node.attributes.category;
34347     }, this);
34348 </code></pre><
34349  * This would pass an HTTP parameter called "category" to the server containing
34350  * the value of the Node's "category" attribute.
34351  * @constructor
34352  * Creates a new Treeloader.
34353  * @param {Object} config A config object containing config properties.
34354  */
34355 Roo.tree.TreeLoader = function(config){
34356     this.baseParams = {};
34357     this.requestMethod = "POST";
34358     Roo.apply(this, config);
34359
34360     this.addEvents({
34361     
34362         /**
34363          * @event beforeload
34364          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34365          * @param {Object} This TreeLoader object.
34366          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34367          * @param {Object} callback The callback function specified in the {@link #load} call.
34368          */
34369         beforeload : true,
34370         /**
34371          * @event load
34372          * Fires when the node has been successfuly loaded.
34373          * @param {Object} This TreeLoader object.
34374          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34375          * @param {Object} response The response object containing the data from the server.
34376          */
34377         load : true,
34378         /**
34379          * @event loadexception
34380          * Fires if the network request failed.
34381          * @param {Object} This TreeLoader object.
34382          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34383          * @param {Object} response The response object containing the data from the server.
34384          */
34385         loadexception : true,
34386         /**
34387          * @event create
34388          * Fires before a node is created, enabling you to return custom Node types 
34389          * @param {Object} This TreeLoader object.
34390          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34391          */
34392         create : true
34393     });
34394
34395     Roo.tree.TreeLoader.superclass.constructor.call(this);
34396 };
34397
34398 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34399     /**
34400     * @cfg {String} dataUrl The URL from which to request a Json string which
34401     * specifies an array of node definition object representing the child nodes
34402     * to be loaded.
34403     */
34404     /**
34405     * @cfg {String} requestMethod either GET or POST
34406     * defaults to POST (due to BC)
34407     * to be loaded.
34408     */
34409     /**
34410     * @cfg {Object} baseParams (optional) An object containing properties which
34411     * specify HTTP parameters to be passed to each request for child nodes.
34412     */
34413     /**
34414     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34415     * created by this loader. If the attributes sent by the server have an attribute in this object,
34416     * they take priority.
34417     */
34418     /**
34419     * @cfg {Object} uiProviders (optional) An object containing properties which
34420     * 
34421     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34422     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34423     * <i>uiProvider</i> attribute of a returned child node is a string rather
34424     * than a reference to a TreeNodeUI implementation, this that string value
34425     * is used as a property name in the uiProviders object. You can define the provider named
34426     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34427     */
34428     uiProviders : {},
34429
34430     /**
34431     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34432     * child nodes before loading.
34433     */
34434     clearOnLoad : true,
34435
34436     /**
34437     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34438     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34439     * Grid query { data : [ .....] }
34440     */
34441     
34442     root : false,
34443      /**
34444     * @cfg {String} queryParam (optional) 
34445     * Name of the query as it will be passed on the querystring (defaults to 'node')
34446     * eg. the request will be ?node=[id]
34447     */
34448     
34449     
34450     queryParam: false,
34451     
34452     /**
34453      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34454      * This is called automatically when a node is expanded, but may be used to reload
34455      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34456      * @param {Roo.tree.TreeNode} node
34457      * @param {Function} callback
34458      */
34459     load : function(node, callback){
34460         if(this.clearOnLoad){
34461             while(node.firstChild){
34462                 node.removeChild(node.firstChild);
34463             }
34464         }
34465         if(node.attributes.children){ // preloaded json children
34466             var cs = node.attributes.children;
34467             for(var i = 0, len = cs.length; i < len; i++){
34468                 node.appendChild(this.createNode(cs[i]));
34469             }
34470             if(typeof callback == "function"){
34471                 callback();
34472             }
34473         }else if(this.dataUrl){
34474             this.requestData(node, callback);
34475         }
34476     },
34477
34478     getParams: function(node){
34479         var buf = [], bp = this.baseParams;
34480         for(var key in bp){
34481             if(typeof bp[key] != "function"){
34482                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34483             }
34484         }
34485         var n = this.queryParam === false ? 'node' : this.queryParam;
34486         buf.push(n + "=", encodeURIComponent(node.id));
34487         return buf.join("");
34488     },
34489
34490     requestData : function(node, callback){
34491         if(this.fireEvent("beforeload", this, node, callback) !== false){
34492             this.transId = Roo.Ajax.request({
34493                 method:this.requestMethod,
34494                 url: this.dataUrl||this.url,
34495                 success: this.handleResponse,
34496                 failure: this.handleFailure,
34497                 scope: this,
34498                 argument: {callback: callback, node: node},
34499                 params: this.getParams(node)
34500             });
34501         }else{
34502             // if the load is cancelled, make sure we notify
34503             // the node that we are done
34504             if(typeof callback == "function"){
34505                 callback();
34506             }
34507         }
34508     },
34509
34510     isLoading : function(){
34511         return this.transId ? true : false;
34512     },
34513
34514     abort : function(){
34515         if(this.isLoading()){
34516             Roo.Ajax.abort(this.transId);
34517         }
34518     },
34519
34520     // private
34521     createNode : function(attr)
34522     {
34523         // apply baseAttrs, nice idea Corey!
34524         if(this.baseAttrs){
34525             Roo.applyIf(attr, this.baseAttrs);
34526         }
34527         if(this.applyLoader !== false){
34528             attr.loader = this;
34529         }
34530         // uiProvider = depreciated..
34531         
34532         if(typeof(attr.uiProvider) == 'string'){
34533            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34534                 /**  eval:var:attr */ eval(attr.uiProvider);
34535         }
34536         if(typeof(this.uiProviders['default']) != 'undefined') {
34537             attr.uiProvider = this.uiProviders['default'];
34538         }
34539         
34540         this.fireEvent('create', this, attr);
34541         
34542         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34543         return(attr.leaf ?
34544                         new Roo.tree.TreeNode(attr) :
34545                         new Roo.tree.AsyncTreeNode(attr));
34546     },
34547
34548     processResponse : function(response, node, callback)
34549     {
34550         var json = response.responseText;
34551         try {
34552             
34553             var o = Roo.decode(json);
34554             
34555             if (this.root === false && typeof(o.success) != undefined) {
34556                 this.root = 'data'; // the default behaviour for list like data..
34557                 }
34558                 
34559             if (this.root !== false &&  !o.success) {
34560                 // it's a failure condition.
34561                 var a = response.argument;
34562                 this.fireEvent("loadexception", this, a.node, response);
34563                 Roo.log("Load failed - should have a handler really");
34564                 return;
34565             }
34566             
34567             
34568             
34569             if (this.root !== false) {
34570                  o = o[this.root];
34571             }
34572             
34573             for(var i = 0, len = o.length; i < len; i++){
34574                 var n = this.createNode(o[i]);
34575                 if(n){
34576                     node.appendChild(n);
34577                 }
34578             }
34579             if(typeof callback == "function"){
34580                 callback(this, node);
34581             }
34582         }catch(e){
34583             this.handleFailure(response);
34584         }
34585     },
34586
34587     handleResponse : function(response){
34588         this.transId = false;
34589         var a = response.argument;
34590         this.processResponse(response, a.node, a.callback);
34591         this.fireEvent("load", this, a.node, response);
34592     },
34593
34594     handleFailure : function(response)
34595     {
34596         // should handle failure better..
34597         this.transId = false;
34598         var a = response.argument;
34599         this.fireEvent("loadexception", this, a.node, response);
34600         if(typeof a.callback == "function"){
34601             a.callback(this, a.node);
34602         }
34603     }
34604 });/*
34605  * Based on:
34606  * Ext JS Library 1.1.1
34607  * Copyright(c) 2006-2007, Ext JS, LLC.
34608  *
34609  * Originally Released Under LGPL - original licence link has changed is not relivant.
34610  *
34611  * Fork - LGPL
34612  * <script type="text/javascript">
34613  */
34614
34615 /**
34616 * @class Roo.tree.TreeFilter
34617 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34618 * @param {TreePanel} tree
34619 * @param {Object} config (optional)
34620  */
34621 Roo.tree.TreeFilter = function(tree, config){
34622     this.tree = tree;
34623     this.filtered = {};
34624     Roo.apply(this, config);
34625 };
34626
34627 Roo.tree.TreeFilter.prototype = {
34628     clearBlank:false,
34629     reverse:false,
34630     autoClear:false,
34631     remove:false,
34632
34633      /**
34634      * Filter the data by a specific attribute.
34635      * @param {String/RegExp} value Either string that the attribute value
34636      * should start with or a RegExp to test against the attribute
34637      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34638      * @param {TreeNode} startNode (optional) The node to start the filter at.
34639      */
34640     filter : function(value, attr, startNode){
34641         attr = attr || "text";
34642         var f;
34643         if(typeof value == "string"){
34644             var vlen = value.length;
34645             // auto clear empty filter
34646             if(vlen == 0 && this.clearBlank){
34647                 this.clear();
34648                 return;
34649             }
34650             value = value.toLowerCase();
34651             f = function(n){
34652                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34653             };
34654         }else if(value.exec){ // regex?
34655             f = function(n){
34656                 return value.test(n.attributes[attr]);
34657             };
34658         }else{
34659             throw 'Illegal filter type, must be string or regex';
34660         }
34661         this.filterBy(f, null, startNode);
34662         },
34663
34664     /**
34665      * Filter by a function. The passed function will be called with each
34666      * node in the tree (or from the startNode). If the function returns true, the node is kept
34667      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34668      * @param {Function} fn The filter function
34669      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34670      */
34671     filterBy : function(fn, scope, startNode){
34672         startNode = startNode || this.tree.root;
34673         if(this.autoClear){
34674             this.clear();
34675         }
34676         var af = this.filtered, rv = this.reverse;
34677         var f = function(n){
34678             if(n == startNode){
34679                 return true;
34680             }
34681             if(af[n.id]){
34682                 return false;
34683             }
34684             var m = fn.call(scope || n, n);
34685             if(!m || rv){
34686                 af[n.id] = n;
34687                 n.ui.hide();
34688                 return false;
34689             }
34690             return true;
34691         };
34692         startNode.cascade(f);
34693         if(this.remove){
34694            for(var id in af){
34695                if(typeof id != "function"){
34696                    var n = af[id];
34697                    if(n && n.parentNode){
34698                        n.parentNode.removeChild(n);
34699                    }
34700                }
34701            }
34702         }
34703     },
34704
34705     /**
34706      * Clears the current filter. Note: with the "remove" option
34707      * set a filter cannot be cleared.
34708      */
34709     clear : function(){
34710         var t = this.tree;
34711         var af = this.filtered;
34712         for(var id in af){
34713             if(typeof id != "function"){
34714                 var n = af[id];
34715                 if(n){
34716                     n.ui.show();
34717                 }
34718             }
34719         }
34720         this.filtered = {};
34721     }
34722 };
34723 /*
34724  * Based on:
34725  * Ext JS Library 1.1.1
34726  * Copyright(c) 2006-2007, Ext JS, LLC.
34727  *
34728  * Originally Released Under LGPL - original licence link has changed is not relivant.
34729  *
34730  * Fork - LGPL
34731  * <script type="text/javascript">
34732  */
34733  
34734
34735 /**
34736  * @class Roo.tree.TreeSorter
34737  * Provides sorting of nodes in a TreePanel
34738  * 
34739  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34740  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34741  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34742  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34743  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34744  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34745  * @constructor
34746  * @param {TreePanel} tree
34747  * @param {Object} config
34748  */
34749 Roo.tree.TreeSorter = function(tree, config){
34750     Roo.apply(this, config);
34751     tree.on("beforechildrenrendered", this.doSort, this);
34752     tree.on("append", this.updateSort, this);
34753     tree.on("insert", this.updateSort, this);
34754     
34755     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34756     var p = this.property || "text";
34757     var sortType = this.sortType;
34758     var fs = this.folderSort;
34759     var cs = this.caseSensitive === true;
34760     var leafAttr = this.leafAttr || 'leaf';
34761
34762     this.sortFn = function(n1, n2){
34763         if(fs){
34764             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34765                 return 1;
34766             }
34767             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34768                 return -1;
34769             }
34770         }
34771         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34772         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34773         if(v1 < v2){
34774                         return dsc ? +1 : -1;
34775                 }else if(v1 > v2){
34776                         return dsc ? -1 : +1;
34777         }else{
34778                 return 0;
34779         }
34780     };
34781 };
34782
34783 Roo.tree.TreeSorter.prototype = {
34784     doSort : function(node){
34785         node.sort(this.sortFn);
34786     },
34787     
34788     compareNodes : function(n1, n2){
34789         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34790     },
34791     
34792     updateSort : function(tree, node){
34793         if(node.childrenRendered){
34794             this.doSort.defer(1, this, [node]);
34795         }
34796     }
34797 };/*
34798  * Based on:
34799  * Ext JS Library 1.1.1
34800  * Copyright(c) 2006-2007, Ext JS, LLC.
34801  *
34802  * Originally Released Under LGPL - original licence link has changed is not relivant.
34803  *
34804  * Fork - LGPL
34805  * <script type="text/javascript">
34806  */
34807
34808 if(Roo.dd.DropZone){
34809     
34810 Roo.tree.TreeDropZone = function(tree, config){
34811     this.allowParentInsert = false;
34812     this.allowContainerDrop = false;
34813     this.appendOnly = false;
34814     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34815     this.tree = tree;
34816     this.lastInsertClass = "x-tree-no-status";
34817     this.dragOverData = {};
34818 };
34819
34820 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34821     ddGroup : "TreeDD",
34822     scroll:  true,
34823     
34824     expandDelay : 1000,
34825     
34826     expandNode : function(node){
34827         if(node.hasChildNodes() && !node.isExpanded()){
34828             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34829         }
34830     },
34831     
34832     queueExpand : function(node){
34833         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34834     },
34835     
34836     cancelExpand : function(){
34837         if(this.expandProcId){
34838             clearTimeout(this.expandProcId);
34839             this.expandProcId = false;
34840         }
34841     },
34842     
34843     isValidDropPoint : function(n, pt, dd, e, data){
34844         if(!n || !data){ return false; }
34845         var targetNode = n.node;
34846         var dropNode = data.node;
34847         // default drop rules
34848         if(!(targetNode && targetNode.isTarget && pt)){
34849             return false;
34850         }
34851         if(pt == "append" && targetNode.allowChildren === false){
34852             return false;
34853         }
34854         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34855             return false;
34856         }
34857         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34858             return false;
34859         }
34860         // reuse the object
34861         var overEvent = this.dragOverData;
34862         overEvent.tree = this.tree;
34863         overEvent.target = targetNode;
34864         overEvent.data = data;
34865         overEvent.point = pt;
34866         overEvent.source = dd;
34867         overEvent.rawEvent = e;
34868         overEvent.dropNode = dropNode;
34869         overEvent.cancel = false;  
34870         var result = this.tree.fireEvent("nodedragover", overEvent);
34871         return overEvent.cancel === false && result !== false;
34872     },
34873     
34874     getDropPoint : function(e, n, dd)
34875     {
34876         var tn = n.node;
34877         if(tn.isRoot){
34878             return tn.allowChildren !== false ? "append" : false; // always append for root
34879         }
34880         var dragEl = n.ddel;
34881         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34882         var y = Roo.lib.Event.getPageY(e);
34883         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34884         
34885         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34886         var noAppend = tn.allowChildren === false;
34887         if(this.appendOnly || tn.parentNode.allowChildren === false){
34888             return noAppend ? false : "append";
34889         }
34890         var noBelow = false;
34891         if(!this.allowParentInsert){
34892             noBelow = tn.hasChildNodes() && tn.isExpanded();
34893         }
34894         var q = (b - t) / (noAppend ? 2 : 3);
34895         if(y >= t && y < (t + q)){
34896             return "above";
34897         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34898             return "below";
34899         }else{
34900             return "append";
34901         }
34902     },
34903     
34904     onNodeEnter : function(n, dd, e, data)
34905     {
34906         this.cancelExpand();
34907     },
34908     
34909     onNodeOver : function(n, dd, e, data)
34910     {
34911        
34912         var pt = this.getDropPoint(e, n, dd);
34913         var node = n.node;
34914         
34915         // auto node expand check
34916         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34917             this.queueExpand(node);
34918         }else if(pt != "append"){
34919             this.cancelExpand();
34920         }
34921         
34922         // set the insert point style on the target node
34923         var returnCls = this.dropNotAllowed;
34924         if(this.isValidDropPoint(n, pt, dd, e, data)){
34925            if(pt){
34926                var el = n.ddel;
34927                var cls;
34928                if(pt == "above"){
34929                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34930                    cls = "x-tree-drag-insert-above";
34931                }else if(pt == "below"){
34932                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34933                    cls = "x-tree-drag-insert-below";
34934                }else{
34935                    returnCls = "x-tree-drop-ok-append";
34936                    cls = "x-tree-drag-append";
34937                }
34938                if(this.lastInsertClass != cls){
34939                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34940                    this.lastInsertClass = cls;
34941                }
34942            }
34943        }
34944        return returnCls;
34945     },
34946     
34947     onNodeOut : function(n, dd, e, data){
34948         
34949         this.cancelExpand();
34950         this.removeDropIndicators(n);
34951     },
34952     
34953     onNodeDrop : function(n, dd, e, data){
34954         var point = this.getDropPoint(e, n, dd);
34955         var targetNode = n.node;
34956         targetNode.ui.startDrop();
34957         if(!this.isValidDropPoint(n, point, dd, e, data)){
34958             targetNode.ui.endDrop();
34959             return false;
34960         }
34961         // first try to find the drop node
34962         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34963         var dropEvent = {
34964             tree : this.tree,
34965             target: targetNode,
34966             data: data,
34967             point: point,
34968             source: dd,
34969             rawEvent: e,
34970             dropNode: dropNode,
34971             cancel: !dropNode   
34972         };
34973         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34974         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34975             targetNode.ui.endDrop();
34976             return false;
34977         }
34978         // allow target changing
34979         targetNode = dropEvent.target;
34980         if(point == "append" && !targetNode.isExpanded()){
34981             targetNode.expand(false, null, function(){
34982                 this.completeDrop(dropEvent);
34983             }.createDelegate(this));
34984         }else{
34985             this.completeDrop(dropEvent);
34986         }
34987         return true;
34988     },
34989     
34990     completeDrop : function(de){
34991         var ns = de.dropNode, p = de.point, t = de.target;
34992         if(!(ns instanceof Array)){
34993             ns = [ns];
34994         }
34995         var n;
34996         for(var i = 0, len = ns.length; i < len; i++){
34997             n = ns[i];
34998             if(p == "above"){
34999                 t.parentNode.insertBefore(n, t);
35000             }else if(p == "below"){
35001                 t.parentNode.insertBefore(n, t.nextSibling);
35002             }else{
35003                 t.appendChild(n);
35004             }
35005         }
35006         n.ui.focus();
35007         if(this.tree.hlDrop){
35008             n.ui.highlight();
35009         }
35010         t.ui.endDrop();
35011         this.tree.fireEvent("nodedrop", de);
35012     },
35013     
35014     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35015         if(this.tree.hlDrop){
35016             dropNode.ui.focus();
35017             dropNode.ui.highlight();
35018         }
35019         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35020     },
35021     
35022     getTree : function(){
35023         return this.tree;
35024     },
35025     
35026     removeDropIndicators : function(n){
35027         if(n && n.ddel){
35028             var el = n.ddel;
35029             Roo.fly(el).removeClass([
35030                     "x-tree-drag-insert-above",
35031                     "x-tree-drag-insert-below",
35032                     "x-tree-drag-append"]);
35033             this.lastInsertClass = "_noclass";
35034         }
35035     },
35036     
35037     beforeDragDrop : function(target, e, id){
35038         this.cancelExpand();
35039         return true;
35040     },
35041     
35042     afterRepair : function(data){
35043         if(data && Roo.enableFx){
35044             data.node.ui.highlight();
35045         }
35046         this.hideProxy();
35047     } 
35048     
35049 });
35050
35051 }
35052 /*
35053  * Based on:
35054  * Ext JS Library 1.1.1
35055  * Copyright(c) 2006-2007, Ext JS, LLC.
35056  *
35057  * Originally Released Under LGPL - original licence link has changed is not relivant.
35058  *
35059  * Fork - LGPL
35060  * <script type="text/javascript">
35061  */
35062  
35063
35064 if(Roo.dd.DragZone){
35065 Roo.tree.TreeDragZone = function(tree, config){
35066     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35067     this.tree = tree;
35068 };
35069
35070 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35071     ddGroup : "TreeDD",
35072    
35073     onBeforeDrag : function(data, e){
35074         var n = data.node;
35075         return n && n.draggable && !n.disabled;
35076     },
35077      
35078     
35079     onInitDrag : function(e){
35080         var data = this.dragData;
35081         this.tree.getSelectionModel().select(data.node);
35082         this.proxy.update("");
35083         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35084         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35085     },
35086     
35087     getRepairXY : function(e, data){
35088         return data.node.ui.getDDRepairXY();
35089     },
35090     
35091     onEndDrag : function(data, e){
35092         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35093         
35094         
35095     },
35096     
35097     onValidDrop : function(dd, e, id){
35098         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35099         this.hideProxy();
35100     },
35101     
35102     beforeInvalidDrop : function(e, id){
35103         // this scrolls the original position back into view
35104         var sm = this.tree.getSelectionModel();
35105         sm.clearSelections();
35106         sm.select(this.dragData.node);
35107     }
35108 });
35109 }/*
35110  * Based on:
35111  * Ext JS Library 1.1.1
35112  * Copyright(c) 2006-2007, Ext JS, LLC.
35113  *
35114  * Originally Released Under LGPL - original licence link has changed is not relivant.
35115  *
35116  * Fork - LGPL
35117  * <script type="text/javascript">
35118  */
35119 /**
35120  * @class Roo.tree.TreeEditor
35121  * @extends Roo.Editor
35122  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35123  * as the editor field.
35124  * @constructor
35125  * @param {Object} config (used to be the tree panel.)
35126  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35127  * 
35128  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35129  * @cfg {Roo.form.TextField|Object} field The field configuration
35130  *
35131  * 
35132  */
35133 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35134     var tree = config;
35135     var field;
35136     if (oldconfig) { // old style..
35137         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35138     } else {
35139         // new style..
35140         tree = config.tree;
35141         config.field = config.field  || {};
35142         config.field.xtype = 'TextField';
35143         field = Roo.factory(config.field, Roo.form);
35144     }
35145     config = config || {};
35146     
35147     
35148     this.addEvents({
35149         /**
35150          * @event beforenodeedit
35151          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35152          * false from the handler of this event.
35153          * @param {Editor} this
35154          * @param {Roo.tree.Node} node 
35155          */
35156         "beforenodeedit" : true
35157     });
35158     
35159     //Roo.log(config);
35160     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35161
35162     this.tree = tree;
35163
35164     tree.on('beforeclick', this.beforeNodeClick, this);
35165     tree.getTreeEl().on('mousedown', this.hide, this);
35166     this.on('complete', this.updateNode, this);
35167     this.on('beforestartedit', this.fitToTree, this);
35168     this.on('startedit', this.bindScroll, this, {delay:10});
35169     this.on('specialkey', this.onSpecialKey, this);
35170 };
35171
35172 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35173     /**
35174      * @cfg {String} alignment
35175      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35176      */
35177     alignment: "l-l",
35178     // inherit
35179     autoSize: false,
35180     /**
35181      * @cfg {Boolean} hideEl
35182      * True to hide the bound element while the editor is displayed (defaults to false)
35183      */
35184     hideEl : false,
35185     /**
35186      * @cfg {String} cls
35187      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35188      */
35189     cls: "x-small-editor x-tree-editor",
35190     /**
35191      * @cfg {Boolean} shim
35192      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35193      */
35194     shim:false,
35195     // inherit
35196     shadow:"frame",
35197     /**
35198      * @cfg {Number} maxWidth
35199      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35200      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35201      * scroll and client offsets into account prior to each edit.
35202      */
35203     maxWidth: 250,
35204
35205     editDelay : 350,
35206
35207     // private
35208     fitToTree : function(ed, el){
35209         var td = this.tree.getTreeEl().dom, nd = el.dom;
35210         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35211             td.scrollLeft = nd.offsetLeft;
35212         }
35213         var w = Math.min(
35214                 this.maxWidth,
35215                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35216         this.setSize(w, '');
35217         
35218         return this.fireEvent('beforenodeedit', this, this.editNode);
35219         
35220     },
35221
35222     // private
35223     triggerEdit : function(node){
35224         this.completeEdit();
35225         this.editNode = node;
35226         this.startEdit(node.ui.textNode, node.text);
35227     },
35228
35229     // private
35230     bindScroll : function(){
35231         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35232     },
35233
35234     // private
35235     beforeNodeClick : function(node, e){
35236         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35237         this.lastClick = new Date();
35238         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35239             e.stopEvent();
35240             this.triggerEdit(node);
35241             return false;
35242         }
35243         return true;
35244     },
35245
35246     // private
35247     updateNode : function(ed, value){
35248         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35249         this.editNode.setText(value);
35250     },
35251
35252     // private
35253     onHide : function(){
35254         Roo.tree.TreeEditor.superclass.onHide.call(this);
35255         if(this.editNode){
35256             this.editNode.ui.focus();
35257         }
35258     },
35259
35260     // private
35261     onSpecialKey : function(field, e){
35262         var k = e.getKey();
35263         if(k == e.ESC){
35264             e.stopEvent();
35265             this.cancelEdit();
35266         }else if(k == e.ENTER && !e.hasModifier()){
35267             e.stopEvent();
35268             this.completeEdit();
35269         }
35270     }
35271 });//<Script type="text/javascript">
35272 /*
35273  * Based on:
35274  * Ext JS Library 1.1.1
35275  * Copyright(c) 2006-2007, Ext JS, LLC.
35276  *
35277  * Originally Released Under LGPL - original licence link has changed is not relivant.
35278  *
35279  * Fork - LGPL
35280  * <script type="text/javascript">
35281  */
35282  
35283 /**
35284  * Not documented??? - probably should be...
35285  */
35286
35287 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35288     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35289     
35290     renderElements : function(n, a, targetNode, bulkRender){
35291         //consel.log("renderElements?");
35292         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35293
35294         var t = n.getOwnerTree();
35295         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35296         
35297         var cols = t.columns;
35298         var bw = t.borderWidth;
35299         var c = cols[0];
35300         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35301          var cb = typeof a.checked == "boolean";
35302         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35303         var colcls = 'x-t-' + tid + '-c0';
35304         var buf = [
35305             '<li class="x-tree-node">',
35306             
35307                 
35308                 '<div class="x-tree-node-el ', a.cls,'">',
35309                     // extran...
35310                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35311                 
35312                 
35313                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35314                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35315                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35316                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35317                            (a.iconCls ? ' '+a.iconCls : ''),
35318                            '" unselectable="on" />',
35319                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35320                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35321                              
35322                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35323                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35324                             '<span unselectable="on" qtip="' + tx + '">',
35325                              tx,
35326                              '</span></a>' ,
35327                     '</div>',
35328                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35329                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35330                  ];
35331         for(var i = 1, len = cols.length; i < len; i++){
35332             c = cols[i];
35333             colcls = 'x-t-' + tid + '-c' +i;
35334             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35335             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35336                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35337                       "</div>");
35338          }
35339          
35340          buf.push(
35341             '</a>',
35342             '<div class="x-clear"></div></div>',
35343             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35344             "</li>");
35345         
35346         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35347             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35348                                 n.nextSibling.ui.getEl(), buf.join(""));
35349         }else{
35350             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35351         }
35352         var el = this.wrap.firstChild;
35353         this.elRow = el;
35354         this.elNode = el.firstChild;
35355         this.ranchor = el.childNodes[1];
35356         this.ctNode = this.wrap.childNodes[1];
35357         var cs = el.firstChild.childNodes;
35358         this.indentNode = cs[0];
35359         this.ecNode = cs[1];
35360         this.iconNode = cs[2];
35361         var index = 3;
35362         if(cb){
35363             this.checkbox = cs[3];
35364             index++;
35365         }
35366         this.anchor = cs[index];
35367         
35368         this.textNode = cs[index].firstChild;
35369         
35370         //el.on("click", this.onClick, this);
35371         //el.on("dblclick", this.onDblClick, this);
35372         
35373         
35374        // console.log(this);
35375     },
35376     initEvents : function(){
35377         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35378         
35379             
35380         var a = this.ranchor;
35381
35382         var el = Roo.get(a);
35383
35384         if(Roo.isOpera){ // opera render bug ignores the CSS
35385             el.setStyle("text-decoration", "none");
35386         }
35387
35388         el.on("click", this.onClick, this);
35389         el.on("dblclick", this.onDblClick, this);
35390         el.on("contextmenu", this.onContextMenu, this);
35391         
35392     },
35393     
35394     /*onSelectedChange : function(state){
35395         if(state){
35396             this.focus();
35397             this.addClass("x-tree-selected");
35398         }else{
35399             //this.blur();
35400             this.removeClass("x-tree-selected");
35401         }
35402     },*/
35403     addClass : function(cls){
35404         if(this.elRow){
35405             Roo.fly(this.elRow).addClass(cls);
35406         }
35407         
35408     },
35409     
35410     
35411     removeClass : function(cls){
35412         if(this.elRow){
35413             Roo.fly(this.elRow).removeClass(cls);
35414         }
35415     }
35416
35417     
35418     
35419 });//<Script type="text/javascript">
35420
35421 /*
35422  * Based on:
35423  * Ext JS Library 1.1.1
35424  * Copyright(c) 2006-2007, Ext JS, LLC.
35425  *
35426  * Originally Released Under LGPL - original licence link has changed is not relivant.
35427  *
35428  * Fork - LGPL
35429  * <script type="text/javascript">
35430  */
35431  
35432
35433 /**
35434  * @class Roo.tree.ColumnTree
35435  * @extends Roo.data.TreePanel
35436  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35437  * @cfg {int} borderWidth  compined right/left border allowance
35438  * @constructor
35439  * @param {String/HTMLElement/Element} el The container element
35440  * @param {Object} config
35441  */
35442 Roo.tree.ColumnTree =  function(el, config)
35443 {
35444    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35445    this.addEvents({
35446         /**
35447         * @event resize
35448         * Fire this event on a container when it resizes
35449         * @param {int} w Width
35450         * @param {int} h Height
35451         */
35452        "resize" : true
35453     });
35454     this.on('resize', this.onResize, this);
35455 };
35456
35457 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35458     //lines:false,
35459     
35460     
35461     borderWidth: Roo.isBorderBox ? 0 : 2, 
35462     headEls : false,
35463     
35464     render : function(){
35465         // add the header.....
35466        
35467         Roo.tree.ColumnTree.superclass.render.apply(this);
35468         
35469         this.el.addClass('x-column-tree');
35470         
35471         this.headers = this.el.createChild(
35472             {cls:'x-tree-headers'},this.innerCt.dom);
35473    
35474         var cols = this.columns, c;
35475         var totalWidth = 0;
35476         this.headEls = [];
35477         var  len = cols.length;
35478         for(var i = 0; i < len; i++){
35479              c = cols[i];
35480              totalWidth += c.width;
35481             this.headEls.push(this.headers.createChild({
35482                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35483                  cn: {
35484                      cls:'x-tree-hd-text',
35485                      html: c.header
35486                  },
35487                  style:'width:'+(c.width-this.borderWidth)+'px;'
35488              }));
35489         }
35490         this.headers.createChild({cls:'x-clear'});
35491         // prevent floats from wrapping when clipped
35492         this.headers.setWidth(totalWidth);
35493         //this.innerCt.setWidth(totalWidth);
35494         this.innerCt.setStyle({ overflow: 'auto' });
35495         this.onResize(this.width, this.height);
35496              
35497         
35498     },
35499     onResize : function(w,h)
35500     {
35501         this.height = h;
35502         this.width = w;
35503         // resize cols..
35504         this.innerCt.setWidth(this.width);
35505         this.innerCt.setHeight(this.height-20);
35506         
35507         // headers...
35508         var cols = this.columns, c;
35509         var totalWidth = 0;
35510         var expEl = false;
35511         var len = cols.length;
35512         for(var i = 0; i < len; i++){
35513             c = cols[i];
35514             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35515                 // it's the expander..
35516                 expEl  = this.headEls[i];
35517                 continue;
35518             }
35519             totalWidth += c.width;
35520             
35521         }
35522         if (expEl) {
35523             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35524         }
35525         this.headers.setWidth(w-20);
35526
35527         
35528         
35529         
35530     }
35531 });
35532 /*
35533  * Based on:
35534  * Ext JS Library 1.1.1
35535  * Copyright(c) 2006-2007, Ext JS, LLC.
35536  *
35537  * Originally Released Under LGPL - original licence link has changed is not relivant.
35538  *
35539  * Fork - LGPL
35540  * <script type="text/javascript">
35541  */
35542  
35543 /**
35544  * @class Roo.menu.Menu
35545  * @extends Roo.util.Observable
35546  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35547  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35548  * @constructor
35549  * Creates a new Menu
35550  * @param {Object} config Configuration options
35551  */
35552 Roo.menu.Menu = function(config){
35553     Roo.apply(this, config);
35554     this.id = this.id || Roo.id();
35555     this.addEvents({
35556         /**
35557          * @event beforeshow
35558          * Fires before this menu is displayed
35559          * @param {Roo.menu.Menu} this
35560          */
35561         beforeshow : true,
35562         /**
35563          * @event beforehide
35564          * Fires before this menu is hidden
35565          * @param {Roo.menu.Menu} this
35566          */
35567         beforehide : true,
35568         /**
35569          * @event show
35570          * Fires after this menu is displayed
35571          * @param {Roo.menu.Menu} this
35572          */
35573         show : true,
35574         /**
35575          * @event hide
35576          * Fires after this menu is hidden
35577          * @param {Roo.menu.Menu} this
35578          */
35579         hide : true,
35580         /**
35581          * @event click
35582          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35583          * @param {Roo.menu.Menu} this
35584          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35585          * @param {Roo.EventObject} e
35586          */
35587         click : true,
35588         /**
35589          * @event mouseover
35590          * Fires when the mouse is hovering over this menu
35591          * @param {Roo.menu.Menu} this
35592          * @param {Roo.EventObject} e
35593          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35594          */
35595         mouseover : true,
35596         /**
35597          * @event mouseout
35598          * Fires when the mouse exits this menu
35599          * @param {Roo.menu.Menu} this
35600          * @param {Roo.EventObject} e
35601          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35602          */
35603         mouseout : true,
35604         /**
35605          * @event itemclick
35606          * Fires when a menu item contained in this menu is clicked
35607          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35608          * @param {Roo.EventObject} e
35609          */
35610         itemclick: true
35611     });
35612     if (this.registerMenu) {
35613         Roo.menu.MenuMgr.register(this);
35614     }
35615     
35616     var mis = this.items;
35617     this.items = new Roo.util.MixedCollection();
35618     if(mis){
35619         this.add.apply(this, mis);
35620     }
35621 };
35622
35623 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35624     /**
35625      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35626      */
35627     minWidth : 120,
35628     /**
35629      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35630      * for bottom-right shadow (defaults to "sides")
35631      */
35632     shadow : "sides",
35633     /**
35634      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35635      * this menu (defaults to "tl-tr?")
35636      */
35637     subMenuAlign : "tl-tr?",
35638     /**
35639      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35640      * relative to its element of origin (defaults to "tl-bl?")
35641      */
35642     defaultAlign : "tl-bl?",
35643     /**
35644      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35645      */
35646     allowOtherMenus : false,
35647     /**
35648      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35649      */
35650     registerMenu : true,
35651
35652     hidden:true,
35653
35654     // private
35655     render : function(){
35656         if(this.el){
35657             return;
35658         }
35659         var el = this.el = new Roo.Layer({
35660             cls: "x-menu",
35661             shadow:this.shadow,
35662             constrain: false,
35663             parentEl: this.parentEl || document.body,
35664             zindex:15000
35665         });
35666
35667         this.keyNav = new Roo.menu.MenuNav(this);
35668
35669         if(this.plain){
35670             el.addClass("x-menu-plain");
35671         }
35672         if(this.cls){
35673             el.addClass(this.cls);
35674         }
35675         // generic focus element
35676         this.focusEl = el.createChild({
35677             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35678         });
35679         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35680         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35681         
35682         ul.on("mouseover", this.onMouseOver, this);
35683         ul.on("mouseout", this.onMouseOut, this);
35684         this.items.each(function(item){
35685             if (item.hidden) {
35686                 return;
35687             }
35688             
35689             var li = document.createElement("li");
35690             li.className = "x-menu-list-item";
35691             ul.dom.appendChild(li);
35692             item.render(li, this);
35693         }, this);
35694         this.ul = ul;
35695         this.autoWidth();
35696     },
35697
35698     // private
35699     autoWidth : function(){
35700         var el = this.el, ul = this.ul;
35701         if(!el){
35702             return;
35703         }
35704         var w = this.width;
35705         if(w){
35706             el.setWidth(w);
35707         }else if(Roo.isIE){
35708             el.setWidth(this.minWidth);
35709             var t = el.dom.offsetWidth; // force recalc
35710             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35711         }
35712     },
35713
35714     // private
35715     delayAutoWidth : function(){
35716         if(this.rendered){
35717             if(!this.awTask){
35718                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35719             }
35720             this.awTask.delay(20);
35721         }
35722     },
35723
35724     // private
35725     findTargetItem : function(e){
35726         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35727         if(t && t.menuItemId){
35728             return this.items.get(t.menuItemId);
35729         }
35730     },
35731
35732     // private
35733     onClick : function(e){
35734         Roo.log("menu.onClick");
35735         var t = this.findTargetItem(e);
35736         if(!t){
35737             return;
35738         }
35739         Roo.log(e);
35740         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35741             if(t == this.activeItem && t.shouldDeactivate(e)){
35742                 this.activeItem.deactivate();
35743                 delete this.activeItem;
35744                 return;
35745             }
35746             if(t.canActivate){
35747                 this.setActiveItem(t, true);
35748             }
35749             return;
35750             
35751             
35752         }
35753         
35754         t.onClick(e);
35755         this.fireEvent("click", this, t, e);
35756     },
35757
35758     // private
35759     setActiveItem : function(item, autoExpand){
35760         if(item != this.activeItem){
35761             if(this.activeItem){
35762                 this.activeItem.deactivate();
35763             }
35764             this.activeItem = item;
35765             item.activate(autoExpand);
35766         }else if(autoExpand){
35767             item.expandMenu();
35768         }
35769     },
35770
35771     // private
35772     tryActivate : function(start, step){
35773         var items = this.items;
35774         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35775             var item = items.get(i);
35776             if(!item.disabled && item.canActivate){
35777                 this.setActiveItem(item, false);
35778                 return item;
35779             }
35780         }
35781         return false;
35782     },
35783
35784     // private
35785     onMouseOver : function(e){
35786         var t;
35787         if(t = this.findTargetItem(e)){
35788             if(t.canActivate && !t.disabled){
35789                 this.setActiveItem(t, true);
35790             }
35791         }
35792         this.fireEvent("mouseover", this, e, t);
35793     },
35794
35795     // private
35796     onMouseOut : function(e){
35797         var t;
35798         if(t = this.findTargetItem(e)){
35799             if(t == this.activeItem && t.shouldDeactivate(e)){
35800                 this.activeItem.deactivate();
35801                 delete this.activeItem;
35802             }
35803         }
35804         this.fireEvent("mouseout", this, e, t);
35805     },
35806
35807     /**
35808      * Read-only.  Returns true if the menu is currently displayed, else false.
35809      * @type Boolean
35810      */
35811     isVisible : function(){
35812         return this.el && !this.hidden;
35813     },
35814
35815     /**
35816      * Displays this menu relative to another element
35817      * @param {String/HTMLElement/Roo.Element} element The element to align to
35818      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35819      * the element (defaults to this.defaultAlign)
35820      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35821      */
35822     show : function(el, pos, parentMenu){
35823         this.parentMenu = parentMenu;
35824         if(!this.el){
35825             this.render();
35826         }
35827         this.fireEvent("beforeshow", this);
35828         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35829     },
35830
35831     /**
35832      * Displays this menu at a specific xy position
35833      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35834      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35835      */
35836     showAt : function(xy, parentMenu, /* private: */_e){
35837         this.parentMenu = parentMenu;
35838         if(!this.el){
35839             this.render();
35840         }
35841         if(_e !== false){
35842             this.fireEvent("beforeshow", this);
35843             xy = this.el.adjustForConstraints(xy);
35844         }
35845         this.el.setXY(xy);
35846         this.el.show();
35847         this.hidden = false;
35848         this.focus();
35849         this.fireEvent("show", this);
35850     },
35851
35852     focus : function(){
35853         if(!this.hidden){
35854             this.doFocus.defer(50, this);
35855         }
35856     },
35857
35858     doFocus : function(){
35859         if(!this.hidden){
35860             this.focusEl.focus();
35861         }
35862     },
35863
35864     /**
35865      * Hides this menu and optionally all parent menus
35866      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35867      */
35868     hide : function(deep){
35869         if(this.el && this.isVisible()){
35870             this.fireEvent("beforehide", this);
35871             if(this.activeItem){
35872                 this.activeItem.deactivate();
35873                 this.activeItem = null;
35874             }
35875             this.el.hide();
35876             this.hidden = true;
35877             this.fireEvent("hide", this);
35878         }
35879         if(deep === true && this.parentMenu){
35880             this.parentMenu.hide(true);
35881         }
35882     },
35883
35884     /**
35885      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35886      * Any of the following are valid:
35887      * <ul>
35888      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35889      * <li>An HTMLElement object which will be converted to a menu item</li>
35890      * <li>A menu item config object that will be created as a new menu item</li>
35891      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35892      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35893      * </ul>
35894      * Usage:
35895      * <pre><code>
35896 // Create the menu
35897 var menu = new Roo.menu.Menu();
35898
35899 // Create a menu item to add by reference
35900 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35901
35902 // Add a bunch of items at once using different methods.
35903 // Only the last item added will be returned.
35904 var item = menu.add(
35905     menuItem,                // add existing item by ref
35906     'Dynamic Item',          // new TextItem
35907     '-',                     // new separator
35908     { text: 'Config Item' }  // new item by config
35909 );
35910 </code></pre>
35911      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35912      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35913      */
35914     add : function(){
35915         var a = arguments, l = a.length, item;
35916         for(var i = 0; i < l; i++){
35917             var el = a[i];
35918             if ((typeof(el) == "object") && el.xtype && el.xns) {
35919                 el = Roo.factory(el, Roo.menu);
35920             }
35921             
35922             if(el.render){ // some kind of Item
35923                 item = this.addItem(el);
35924             }else if(typeof el == "string"){ // string
35925                 if(el == "separator" || el == "-"){
35926                     item = this.addSeparator();
35927                 }else{
35928                     item = this.addText(el);
35929                 }
35930             }else if(el.tagName || el.el){ // element
35931                 item = this.addElement(el);
35932             }else if(typeof el == "object"){ // must be menu item config?
35933                 item = this.addMenuItem(el);
35934             }
35935         }
35936         return item;
35937     },
35938
35939     /**
35940      * Returns this menu's underlying {@link Roo.Element} object
35941      * @return {Roo.Element} The element
35942      */
35943     getEl : function(){
35944         if(!this.el){
35945             this.render();
35946         }
35947         return this.el;
35948     },
35949
35950     /**
35951      * Adds a separator bar to the menu
35952      * @return {Roo.menu.Item} The menu item that was added
35953      */
35954     addSeparator : function(){
35955         return this.addItem(new Roo.menu.Separator());
35956     },
35957
35958     /**
35959      * Adds an {@link Roo.Element} object to the menu
35960      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35961      * @return {Roo.menu.Item} The menu item that was added
35962      */
35963     addElement : function(el){
35964         return this.addItem(new Roo.menu.BaseItem(el));
35965     },
35966
35967     /**
35968      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35969      * @param {Roo.menu.Item} item The menu item to add
35970      * @return {Roo.menu.Item} The menu item that was added
35971      */
35972     addItem : function(item){
35973         this.items.add(item);
35974         if(this.ul){
35975             var li = document.createElement("li");
35976             li.className = "x-menu-list-item";
35977             this.ul.dom.appendChild(li);
35978             item.render(li, this);
35979             this.delayAutoWidth();
35980         }
35981         return item;
35982     },
35983
35984     /**
35985      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35986      * @param {Object} config A MenuItem config object
35987      * @return {Roo.menu.Item} The menu item that was added
35988      */
35989     addMenuItem : function(config){
35990         if(!(config instanceof Roo.menu.Item)){
35991             if(typeof config.checked == "boolean"){ // must be check menu item config?
35992                 config = new Roo.menu.CheckItem(config);
35993             }else{
35994                 config = new Roo.menu.Item(config);
35995             }
35996         }
35997         return this.addItem(config);
35998     },
35999
36000     /**
36001      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36002      * @param {String} text The text to display in the menu item
36003      * @return {Roo.menu.Item} The menu item that was added
36004      */
36005     addText : function(text){
36006         return this.addItem(new Roo.menu.TextItem({ text : text }));
36007     },
36008
36009     /**
36010      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36011      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36012      * @param {Roo.menu.Item} item The menu item to add
36013      * @return {Roo.menu.Item} The menu item that was added
36014      */
36015     insert : function(index, item){
36016         this.items.insert(index, item);
36017         if(this.ul){
36018             var li = document.createElement("li");
36019             li.className = "x-menu-list-item";
36020             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36021             item.render(li, this);
36022             this.delayAutoWidth();
36023         }
36024         return item;
36025     },
36026
36027     /**
36028      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36029      * @param {Roo.menu.Item} item The menu item to remove
36030      */
36031     remove : function(item){
36032         this.items.removeKey(item.id);
36033         item.destroy();
36034     },
36035
36036     /**
36037      * Removes and destroys all items in the menu
36038      */
36039     removeAll : function(){
36040         var f;
36041         while(f = this.items.first()){
36042             this.remove(f);
36043         }
36044     }
36045 });
36046
36047 // MenuNav is a private utility class used internally by the Menu
36048 Roo.menu.MenuNav = function(menu){
36049     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36050     this.scope = this.menu = menu;
36051 };
36052
36053 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36054     doRelay : function(e, h){
36055         var k = e.getKey();
36056         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36057             this.menu.tryActivate(0, 1);
36058             return false;
36059         }
36060         return h.call(this.scope || this, e, this.menu);
36061     },
36062
36063     up : function(e, m){
36064         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36065             m.tryActivate(m.items.length-1, -1);
36066         }
36067     },
36068
36069     down : function(e, m){
36070         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36071             m.tryActivate(0, 1);
36072         }
36073     },
36074
36075     right : function(e, m){
36076         if(m.activeItem){
36077             m.activeItem.expandMenu(true);
36078         }
36079     },
36080
36081     left : function(e, m){
36082         m.hide();
36083         if(m.parentMenu && m.parentMenu.activeItem){
36084             m.parentMenu.activeItem.activate();
36085         }
36086     },
36087
36088     enter : function(e, m){
36089         if(m.activeItem){
36090             e.stopPropagation();
36091             m.activeItem.onClick(e);
36092             m.fireEvent("click", this, m.activeItem);
36093             return true;
36094         }
36095     }
36096 });/*
36097  * Based on:
36098  * Ext JS Library 1.1.1
36099  * Copyright(c) 2006-2007, Ext JS, LLC.
36100  *
36101  * Originally Released Under LGPL - original licence link has changed is not relivant.
36102  *
36103  * Fork - LGPL
36104  * <script type="text/javascript">
36105  */
36106  
36107 /**
36108  * @class Roo.menu.MenuMgr
36109  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36110  * @singleton
36111  */
36112 Roo.menu.MenuMgr = function(){
36113    var menus, active, groups = {}, attached = false, lastShow = new Date();
36114
36115    // private - called when first menu is created
36116    function init(){
36117        menus = {};
36118        active = new Roo.util.MixedCollection();
36119        Roo.get(document).addKeyListener(27, function(){
36120            if(active.length > 0){
36121                hideAll();
36122            }
36123        });
36124    }
36125
36126    // private
36127    function hideAll(){
36128        if(active && active.length > 0){
36129            var c = active.clone();
36130            c.each(function(m){
36131                m.hide();
36132            });
36133        }
36134    }
36135
36136    // private
36137    function onHide(m){
36138        active.remove(m);
36139        if(active.length < 1){
36140            Roo.get(document).un("mousedown", onMouseDown);
36141            attached = false;
36142        }
36143    }
36144
36145    // private
36146    function onShow(m){
36147        var last = active.last();
36148        lastShow = new Date();
36149        active.add(m);
36150        if(!attached){
36151            Roo.get(document).on("mousedown", onMouseDown);
36152            attached = true;
36153        }
36154        if(m.parentMenu){
36155           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36156           m.parentMenu.activeChild = m;
36157        }else if(last && last.isVisible()){
36158           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36159        }
36160    }
36161
36162    // private
36163    function onBeforeHide(m){
36164        if(m.activeChild){
36165            m.activeChild.hide();
36166        }
36167        if(m.autoHideTimer){
36168            clearTimeout(m.autoHideTimer);
36169            delete m.autoHideTimer;
36170        }
36171    }
36172
36173    // private
36174    function onBeforeShow(m){
36175        var pm = m.parentMenu;
36176        if(!pm && !m.allowOtherMenus){
36177            hideAll();
36178        }else if(pm && pm.activeChild && active != m){
36179            pm.activeChild.hide();
36180        }
36181    }
36182
36183    // private
36184    function onMouseDown(e){
36185        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36186            hideAll();
36187        }
36188    }
36189
36190    // private
36191    function onBeforeCheck(mi, state){
36192        if(state){
36193            var g = groups[mi.group];
36194            for(var i = 0, l = g.length; i < l; i++){
36195                if(g[i] != mi){
36196                    g[i].setChecked(false);
36197                }
36198            }
36199        }
36200    }
36201
36202    return {
36203
36204        /**
36205         * Hides all menus that are currently visible
36206         */
36207        hideAll : function(){
36208             hideAll();  
36209        },
36210
36211        // private
36212        register : function(menu){
36213            if(!menus){
36214                init();
36215            }
36216            menus[menu.id] = menu;
36217            menu.on("beforehide", onBeforeHide);
36218            menu.on("hide", onHide);
36219            menu.on("beforeshow", onBeforeShow);
36220            menu.on("show", onShow);
36221            var g = menu.group;
36222            if(g && menu.events["checkchange"]){
36223                if(!groups[g]){
36224                    groups[g] = [];
36225                }
36226                groups[g].push(menu);
36227                menu.on("checkchange", onCheck);
36228            }
36229        },
36230
36231         /**
36232          * Returns a {@link Roo.menu.Menu} object
36233          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36234          * be used to generate and return a new Menu instance.
36235          */
36236        get : function(menu){
36237            if(typeof menu == "string"){ // menu id
36238                return menus[menu];
36239            }else if(menu.events){  // menu instance
36240                return menu;
36241            }else if(typeof menu.length == 'number'){ // array of menu items?
36242                return new Roo.menu.Menu({items:menu});
36243            }else{ // otherwise, must be a config
36244                return new Roo.menu.Menu(menu);
36245            }
36246        },
36247
36248        // private
36249        unregister : function(menu){
36250            delete menus[menu.id];
36251            menu.un("beforehide", onBeforeHide);
36252            menu.un("hide", onHide);
36253            menu.un("beforeshow", onBeforeShow);
36254            menu.un("show", onShow);
36255            var g = menu.group;
36256            if(g && menu.events["checkchange"]){
36257                groups[g].remove(menu);
36258                menu.un("checkchange", onCheck);
36259            }
36260        },
36261
36262        // private
36263        registerCheckable : function(menuItem){
36264            var g = menuItem.group;
36265            if(g){
36266                if(!groups[g]){
36267                    groups[g] = [];
36268                }
36269                groups[g].push(menuItem);
36270                menuItem.on("beforecheckchange", onBeforeCheck);
36271            }
36272        },
36273
36274        // private
36275        unregisterCheckable : function(menuItem){
36276            var g = menuItem.group;
36277            if(g){
36278                groups[g].remove(menuItem);
36279                menuItem.un("beforecheckchange", onBeforeCheck);
36280            }
36281        }
36282    };
36283 }();/*
36284  * Based on:
36285  * Ext JS Library 1.1.1
36286  * Copyright(c) 2006-2007, Ext JS, LLC.
36287  *
36288  * Originally Released Under LGPL - original licence link has changed is not relivant.
36289  *
36290  * Fork - LGPL
36291  * <script type="text/javascript">
36292  */
36293  
36294
36295 /**
36296  * @class Roo.menu.BaseItem
36297  * @extends Roo.Component
36298  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36299  * management and base configuration options shared by all menu components.
36300  * @constructor
36301  * Creates a new BaseItem
36302  * @param {Object} config Configuration options
36303  */
36304 Roo.menu.BaseItem = function(config){
36305     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36306
36307     this.addEvents({
36308         /**
36309          * @event click
36310          * Fires when this item is clicked
36311          * @param {Roo.menu.BaseItem} this
36312          * @param {Roo.EventObject} e
36313          */
36314         click: true,
36315         /**
36316          * @event activate
36317          * Fires when this item is activated
36318          * @param {Roo.menu.BaseItem} this
36319          */
36320         activate : true,
36321         /**
36322          * @event deactivate
36323          * Fires when this item is deactivated
36324          * @param {Roo.menu.BaseItem} this
36325          */
36326         deactivate : true
36327     });
36328
36329     if(this.handler){
36330         this.on("click", this.handler, this.scope, true);
36331     }
36332 };
36333
36334 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36335     /**
36336      * @cfg {Function} handler
36337      * A function that will handle the click event of this menu item (defaults to undefined)
36338      */
36339     /**
36340      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36341      */
36342     canActivate : false,
36343     
36344      /**
36345      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36346      */
36347     hidden: false,
36348     
36349     /**
36350      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36351      */
36352     activeClass : "x-menu-item-active",
36353     /**
36354      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36355      */
36356     hideOnClick : true,
36357     /**
36358      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36359      */
36360     hideDelay : 100,
36361
36362     // private
36363     ctype: "Roo.menu.BaseItem",
36364
36365     // private
36366     actionMode : "container",
36367
36368     // private
36369     render : function(container, parentMenu){
36370         this.parentMenu = parentMenu;
36371         Roo.menu.BaseItem.superclass.render.call(this, container);
36372         this.container.menuItemId = this.id;
36373     },
36374
36375     // private
36376     onRender : function(container, position){
36377         this.el = Roo.get(this.el);
36378         container.dom.appendChild(this.el.dom);
36379     },
36380
36381     // private
36382     onClick : function(e){
36383         if(!this.disabled && this.fireEvent("click", this, e) !== false
36384                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36385             this.handleClick(e);
36386         }else{
36387             e.stopEvent();
36388         }
36389     },
36390
36391     // private
36392     activate : function(){
36393         if(this.disabled){
36394             return false;
36395         }
36396         var li = this.container;
36397         li.addClass(this.activeClass);
36398         this.region = li.getRegion().adjust(2, 2, -2, -2);
36399         this.fireEvent("activate", this);
36400         return true;
36401     },
36402
36403     // private
36404     deactivate : function(){
36405         this.container.removeClass(this.activeClass);
36406         this.fireEvent("deactivate", this);
36407     },
36408
36409     // private
36410     shouldDeactivate : function(e){
36411         return !this.region || !this.region.contains(e.getPoint());
36412     },
36413
36414     // private
36415     handleClick : function(e){
36416         if(this.hideOnClick){
36417             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36418         }
36419     },
36420
36421     // private
36422     expandMenu : function(autoActivate){
36423         // do nothing
36424     },
36425
36426     // private
36427     hideMenu : function(){
36428         // do nothing
36429     }
36430 });/*
36431  * Based on:
36432  * Ext JS Library 1.1.1
36433  * Copyright(c) 2006-2007, Ext JS, LLC.
36434  *
36435  * Originally Released Under LGPL - original licence link has changed is not relivant.
36436  *
36437  * Fork - LGPL
36438  * <script type="text/javascript">
36439  */
36440  
36441 /**
36442  * @class Roo.menu.Adapter
36443  * @extends Roo.menu.BaseItem
36444  * 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.
36445  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36446  * @constructor
36447  * Creates a new Adapter
36448  * @param {Object} config Configuration options
36449  */
36450 Roo.menu.Adapter = function(component, config){
36451     Roo.menu.Adapter.superclass.constructor.call(this, config);
36452     this.component = component;
36453 };
36454 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36455     // private
36456     canActivate : true,
36457
36458     // private
36459     onRender : function(container, position){
36460         this.component.render(container);
36461         this.el = this.component.getEl();
36462     },
36463
36464     // private
36465     activate : function(){
36466         if(this.disabled){
36467             return false;
36468         }
36469         this.component.focus();
36470         this.fireEvent("activate", this);
36471         return true;
36472     },
36473
36474     // private
36475     deactivate : function(){
36476         this.fireEvent("deactivate", this);
36477     },
36478
36479     // private
36480     disable : function(){
36481         this.component.disable();
36482         Roo.menu.Adapter.superclass.disable.call(this);
36483     },
36484
36485     // private
36486     enable : function(){
36487         this.component.enable();
36488         Roo.menu.Adapter.superclass.enable.call(this);
36489     }
36490 });/*
36491  * Based on:
36492  * Ext JS Library 1.1.1
36493  * Copyright(c) 2006-2007, Ext JS, LLC.
36494  *
36495  * Originally Released Under LGPL - original licence link has changed is not relivant.
36496  *
36497  * Fork - LGPL
36498  * <script type="text/javascript">
36499  */
36500
36501 /**
36502  * @class Roo.menu.TextItem
36503  * @extends Roo.menu.BaseItem
36504  * Adds a static text string to a menu, usually used as either a heading or group separator.
36505  * Note: old style constructor with text is still supported.
36506  * 
36507  * @constructor
36508  * Creates a new TextItem
36509  * @param {Object} cfg Configuration
36510  */
36511 Roo.menu.TextItem = function(cfg){
36512     if (typeof(cfg) == 'string') {
36513         this.text = cfg;
36514     } else {
36515         Roo.apply(this,cfg);
36516     }
36517     
36518     Roo.menu.TextItem.superclass.constructor.call(this);
36519 };
36520
36521 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36522     /**
36523      * @cfg {Boolean} text Text to show on item.
36524      */
36525     text : '',
36526     
36527     /**
36528      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36529      */
36530     hideOnClick : false,
36531     /**
36532      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36533      */
36534     itemCls : "x-menu-text",
36535
36536     // private
36537     onRender : function(){
36538         var s = document.createElement("span");
36539         s.className = this.itemCls;
36540         s.innerHTML = this.text;
36541         this.el = s;
36542         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36543     }
36544 });/*
36545  * Based on:
36546  * Ext JS Library 1.1.1
36547  * Copyright(c) 2006-2007, Ext JS, LLC.
36548  *
36549  * Originally Released Under LGPL - original licence link has changed is not relivant.
36550  *
36551  * Fork - LGPL
36552  * <script type="text/javascript">
36553  */
36554
36555 /**
36556  * @class Roo.menu.Separator
36557  * @extends Roo.menu.BaseItem
36558  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36559  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36560  * @constructor
36561  * @param {Object} config Configuration options
36562  */
36563 Roo.menu.Separator = function(config){
36564     Roo.menu.Separator.superclass.constructor.call(this, config);
36565 };
36566
36567 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36568     /**
36569      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36570      */
36571     itemCls : "x-menu-sep",
36572     /**
36573      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36574      */
36575     hideOnClick : false,
36576
36577     // private
36578     onRender : function(li){
36579         var s = document.createElement("span");
36580         s.className = this.itemCls;
36581         s.innerHTML = "&#160;";
36582         this.el = s;
36583         li.addClass("x-menu-sep-li");
36584         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36585     }
36586 });/*
36587  * Based on:
36588  * Ext JS Library 1.1.1
36589  * Copyright(c) 2006-2007, Ext JS, LLC.
36590  *
36591  * Originally Released Under LGPL - original licence link has changed is not relivant.
36592  *
36593  * Fork - LGPL
36594  * <script type="text/javascript">
36595  */
36596 /**
36597  * @class Roo.menu.Item
36598  * @extends Roo.menu.BaseItem
36599  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36600  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36601  * activation and click handling.
36602  * @constructor
36603  * Creates a new Item
36604  * @param {Object} config Configuration options
36605  */
36606 Roo.menu.Item = function(config){
36607     Roo.menu.Item.superclass.constructor.call(this, config);
36608     if(this.menu){
36609         this.menu = Roo.menu.MenuMgr.get(this.menu);
36610     }
36611 };
36612 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36613     
36614     /**
36615      * @cfg {String} text
36616      * The text to show on the menu item.
36617      */
36618     text: '',
36619      /**
36620      * @cfg {String} HTML to render in menu
36621      * The text to show on the menu item (HTML version).
36622      */
36623     html: '',
36624     /**
36625      * @cfg {String} icon
36626      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36627      */
36628     icon: undefined,
36629     /**
36630      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36631      */
36632     itemCls : "x-menu-item",
36633     /**
36634      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36635      */
36636     canActivate : true,
36637     /**
36638      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36639      */
36640     showDelay: 200,
36641     // doc'd in BaseItem
36642     hideDelay: 200,
36643
36644     // private
36645     ctype: "Roo.menu.Item",
36646     
36647     // private
36648     onRender : function(container, position){
36649         var el = document.createElement("a");
36650         el.hideFocus = true;
36651         el.unselectable = "on";
36652         el.href = this.href || "#";
36653         if(this.hrefTarget){
36654             el.target = this.hrefTarget;
36655         }
36656         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36657         
36658         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36659         
36660         el.innerHTML = String.format(
36661                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36662                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36663         this.el = el;
36664         Roo.menu.Item.superclass.onRender.call(this, container, position);
36665     },
36666
36667     /**
36668      * Sets the text to display in this menu item
36669      * @param {String} text The text to display
36670      * @param {Boolean} isHTML true to indicate text is pure html.
36671      */
36672     setText : function(text, isHTML){
36673         if (isHTML) {
36674             this.html = text;
36675         } else {
36676             this.text = text;
36677             this.html = '';
36678         }
36679         if(this.rendered){
36680             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36681      
36682             this.el.update(String.format(
36683                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36684                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36685             this.parentMenu.autoWidth();
36686         }
36687     },
36688
36689     // private
36690     handleClick : function(e){
36691         if(!this.href){ // if no link defined, stop the event automatically
36692             e.stopEvent();
36693         }
36694         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36695     },
36696
36697     // private
36698     activate : function(autoExpand){
36699         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36700             this.focus();
36701             if(autoExpand){
36702                 this.expandMenu();
36703             }
36704         }
36705         return true;
36706     },
36707
36708     // private
36709     shouldDeactivate : function(e){
36710         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36711             if(this.menu && this.menu.isVisible()){
36712                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36713             }
36714             return true;
36715         }
36716         return false;
36717     },
36718
36719     // private
36720     deactivate : function(){
36721         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36722         this.hideMenu();
36723     },
36724
36725     // private
36726     expandMenu : function(autoActivate){
36727         if(!this.disabled && this.menu){
36728             clearTimeout(this.hideTimer);
36729             delete this.hideTimer;
36730             if(!this.menu.isVisible() && !this.showTimer){
36731                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36732             }else if (this.menu.isVisible() && autoActivate){
36733                 this.menu.tryActivate(0, 1);
36734             }
36735         }
36736     },
36737
36738     // private
36739     deferExpand : function(autoActivate){
36740         delete this.showTimer;
36741         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36742         if(autoActivate){
36743             this.menu.tryActivate(0, 1);
36744         }
36745     },
36746
36747     // private
36748     hideMenu : function(){
36749         clearTimeout(this.showTimer);
36750         delete this.showTimer;
36751         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36752             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36753         }
36754     },
36755
36756     // private
36757     deferHide : function(){
36758         delete this.hideTimer;
36759         this.menu.hide();
36760     }
36761 });/*
36762  * Based on:
36763  * Ext JS Library 1.1.1
36764  * Copyright(c) 2006-2007, Ext JS, LLC.
36765  *
36766  * Originally Released Under LGPL - original licence link has changed is not relivant.
36767  *
36768  * Fork - LGPL
36769  * <script type="text/javascript">
36770  */
36771  
36772 /**
36773  * @class Roo.menu.CheckItem
36774  * @extends Roo.menu.Item
36775  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36776  * @constructor
36777  * Creates a new CheckItem
36778  * @param {Object} config Configuration options
36779  */
36780 Roo.menu.CheckItem = function(config){
36781     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36782     this.addEvents({
36783         /**
36784          * @event beforecheckchange
36785          * Fires before the checked value is set, providing an opportunity to cancel if needed
36786          * @param {Roo.menu.CheckItem} this
36787          * @param {Boolean} checked The new checked value that will be set
36788          */
36789         "beforecheckchange" : true,
36790         /**
36791          * @event checkchange
36792          * Fires after the checked value has been set
36793          * @param {Roo.menu.CheckItem} this
36794          * @param {Boolean} checked The checked value that was set
36795          */
36796         "checkchange" : true
36797     });
36798     if(this.checkHandler){
36799         this.on('checkchange', this.checkHandler, this.scope);
36800     }
36801 };
36802 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36803     /**
36804      * @cfg {String} group
36805      * All check items with the same group name will automatically be grouped into a single-select
36806      * radio button group (defaults to '')
36807      */
36808     /**
36809      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36810      */
36811     itemCls : "x-menu-item x-menu-check-item",
36812     /**
36813      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36814      */
36815     groupClass : "x-menu-group-item",
36816
36817     /**
36818      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36819      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36820      * initialized with checked = true will be rendered as checked.
36821      */
36822     checked: false,
36823
36824     // private
36825     ctype: "Roo.menu.CheckItem",
36826
36827     // private
36828     onRender : function(c){
36829         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36830         if(this.group){
36831             this.el.addClass(this.groupClass);
36832         }
36833         Roo.menu.MenuMgr.registerCheckable(this);
36834         if(this.checked){
36835             this.checked = false;
36836             this.setChecked(true, true);
36837         }
36838     },
36839
36840     // private
36841     destroy : function(){
36842         if(this.rendered){
36843             Roo.menu.MenuMgr.unregisterCheckable(this);
36844         }
36845         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36846     },
36847
36848     /**
36849      * Set the checked state of this item
36850      * @param {Boolean} checked The new checked value
36851      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36852      */
36853     setChecked : function(state, suppressEvent){
36854         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36855             if(this.container){
36856                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36857             }
36858             this.checked = state;
36859             if(suppressEvent !== true){
36860                 this.fireEvent("checkchange", this, state);
36861             }
36862         }
36863     },
36864
36865     // private
36866     handleClick : function(e){
36867        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36868            this.setChecked(!this.checked);
36869        }
36870        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36871     }
36872 });/*
36873  * Based on:
36874  * Ext JS Library 1.1.1
36875  * Copyright(c) 2006-2007, Ext JS, LLC.
36876  *
36877  * Originally Released Under LGPL - original licence link has changed is not relivant.
36878  *
36879  * Fork - LGPL
36880  * <script type="text/javascript">
36881  */
36882  
36883 /**
36884  * @class Roo.menu.DateItem
36885  * @extends Roo.menu.Adapter
36886  * A menu item that wraps the {@link Roo.DatPicker} component.
36887  * @constructor
36888  * Creates a new DateItem
36889  * @param {Object} config Configuration options
36890  */
36891 Roo.menu.DateItem = function(config){
36892     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36893     /** The Roo.DatePicker object @type Roo.DatePicker */
36894     this.picker = this.component;
36895     this.addEvents({select: true});
36896     
36897     this.picker.on("render", function(picker){
36898         picker.getEl().swallowEvent("click");
36899         picker.container.addClass("x-menu-date-item");
36900     });
36901
36902     this.picker.on("select", this.onSelect, this);
36903 };
36904
36905 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36906     // private
36907     onSelect : function(picker, date){
36908         this.fireEvent("select", this, date, picker);
36909         Roo.menu.DateItem.superclass.handleClick.call(this);
36910     }
36911 });/*
36912  * Based on:
36913  * Ext JS Library 1.1.1
36914  * Copyright(c) 2006-2007, Ext JS, LLC.
36915  *
36916  * Originally Released Under LGPL - original licence link has changed is not relivant.
36917  *
36918  * Fork - LGPL
36919  * <script type="text/javascript">
36920  */
36921  
36922 /**
36923  * @class Roo.menu.ColorItem
36924  * @extends Roo.menu.Adapter
36925  * A menu item that wraps the {@link Roo.ColorPalette} component.
36926  * @constructor
36927  * Creates a new ColorItem
36928  * @param {Object} config Configuration options
36929  */
36930 Roo.menu.ColorItem = function(config){
36931     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36932     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36933     this.palette = this.component;
36934     this.relayEvents(this.palette, ["select"]);
36935     if(this.selectHandler){
36936         this.on('select', this.selectHandler, this.scope);
36937     }
36938 };
36939 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36940  * Based on:
36941  * Ext JS Library 1.1.1
36942  * Copyright(c) 2006-2007, Ext JS, LLC.
36943  *
36944  * Originally Released Under LGPL - original licence link has changed is not relivant.
36945  *
36946  * Fork - LGPL
36947  * <script type="text/javascript">
36948  */
36949  
36950
36951 /**
36952  * @class Roo.menu.DateMenu
36953  * @extends Roo.menu.Menu
36954  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36955  * @constructor
36956  * Creates a new DateMenu
36957  * @param {Object} config Configuration options
36958  */
36959 Roo.menu.DateMenu = function(config){
36960     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36961     this.plain = true;
36962     var di = new Roo.menu.DateItem(config);
36963     this.add(di);
36964     /**
36965      * The {@link Roo.DatePicker} instance for this DateMenu
36966      * @type DatePicker
36967      */
36968     this.picker = di.picker;
36969     /**
36970      * @event select
36971      * @param {DatePicker} picker
36972      * @param {Date} date
36973      */
36974     this.relayEvents(di, ["select"]);
36975     this.on('beforeshow', function(){
36976         if(this.picker){
36977             this.picker.hideMonthPicker(false);
36978         }
36979     }, this);
36980 };
36981 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36982     cls:'x-date-menu'
36983 });/*
36984  * Based on:
36985  * Ext JS Library 1.1.1
36986  * Copyright(c) 2006-2007, Ext JS, LLC.
36987  *
36988  * Originally Released Under LGPL - original licence link has changed is not relivant.
36989  *
36990  * Fork - LGPL
36991  * <script type="text/javascript">
36992  */
36993  
36994
36995 /**
36996  * @class Roo.menu.ColorMenu
36997  * @extends Roo.menu.Menu
36998  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36999  * @constructor
37000  * Creates a new ColorMenu
37001  * @param {Object} config Configuration options
37002  */
37003 Roo.menu.ColorMenu = function(config){
37004     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37005     this.plain = true;
37006     var ci = new Roo.menu.ColorItem(config);
37007     this.add(ci);
37008     /**
37009      * The {@link Roo.ColorPalette} instance for this ColorMenu
37010      * @type ColorPalette
37011      */
37012     this.palette = ci.palette;
37013     /**
37014      * @event select
37015      * @param {ColorPalette} palette
37016      * @param {String} color
37017      */
37018     this.relayEvents(ci, ["select"]);
37019 };
37020 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37021  * Based on:
37022  * Ext JS Library 1.1.1
37023  * Copyright(c) 2006-2007, Ext JS, LLC.
37024  *
37025  * Originally Released Under LGPL - original licence link has changed is not relivant.
37026  *
37027  * Fork - LGPL
37028  * <script type="text/javascript">
37029  */
37030  
37031 /**
37032  * @class Roo.form.Field
37033  * @extends Roo.BoxComponent
37034  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37035  * @constructor
37036  * Creates a new Field
37037  * @param {Object} config Configuration options
37038  */
37039 Roo.form.Field = function(config){
37040     Roo.form.Field.superclass.constructor.call(this, config);
37041 };
37042
37043 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37044     /**
37045      * @cfg {String} fieldLabel Label to use when rendering a form.
37046      */
37047        /**
37048      * @cfg {String} qtip Mouse over tip
37049      */
37050      
37051     /**
37052      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37053      */
37054     invalidClass : "x-form-invalid",
37055     /**
37056      * @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")
37057      */
37058     invalidText : "The value in this field is invalid",
37059     /**
37060      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37061      */
37062     focusClass : "x-form-focus",
37063     /**
37064      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37065       automatic validation (defaults to "keyup").
37066      */
37067     validationEvent : "keyup",
37068     /**
37069      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37070      */
37071     validateOnBlur : true,
37072     /**
37073      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37074      */
37075     validationDelay : 250,
37076     /**
37077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37078      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37079      */
37080     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37081     /**
37082      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37083      */
37084     fieldClass : "x-form-field",
37085     /**
37086      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37087      *<pre>
37088 Value         Description
37089 -----------   ----------------------------------------------------------------------
37090 qtip          Display a quick tip when the user hovers over the field
37091 title         Display a default browser title attribute popup
37092 under         Add a block div beneath the field containing the error text
37093 side          Add an error icon to the right of the field with a popup on hover
37094 [element id]  Add the error text directly to the innerHTML of the specified element
37095 </pre>
37096      */
37097     msgTarget : 'qtip',
37098     /**
37099      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37100      */
37101     msgFx : 'normal',
37102
37103     /**
37104      * @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.
37105      */
37106     readOnly : false,
37107
37108     /**
37109      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37110      */
37111     disabled : false,
37112
37113     /**
37114      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37115      */
37116     inputType : undefined,
37117     
37118     /**
37119      * @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).
37120          */
37121         tabIndex : undefined,
37122         
37123     // private
37124     isFormField : true,
37125
37126     // private
37127     hasFocus : false,
37128     /**
37129      * @property {Roo.Element} fieldEl
37130      * Element Containing the rendered Field (with label etc.)
37131      */
37132     /**
37133      * @cfg {Mixed} value A value to initialize this field with.
37134      */
37135     value : undefined,
37136
37137     /**
37138      * @cfg {String} name The field's HTML name attribute.
37139      */
37140     /**
37141      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37142      */
37143
37144         // private ??
37145         initComponent : function(){
37146         Roo.form.Field.superclass.initComponent.call(this);
37147         this.addEvents({
37148             /**
37149              * @event focus
37150              * Fires when this field receives input focus.
37151              * @param {Roo.form.Field} this
37152              */
37153             focus : true,
37154             /**
37155              * @event blur
37156              * Fires when this field loses input focus.
37157              * @param {Roo.form.Field} this
37158              */
37159             blur : true,
37160             /**
37161              * @event specialkey
37162              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37163              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37164              * @param {Roo.form.Field} this
37165              * @param {Roo.EventObject} e The event object
37166              */
37167             specialkey : true,
37168             /**
37169              * @event change
37170              * Fires just before the field blurs if the field value has changed.
37171              * @param {Roo.form.Field} this
37172              * @param {Mixed} newValue The new value
37173              * @param {Mixed} oldValue The original value
37174              */
37175             change : true,
37176             /**
37177              * @event invalid
37178              * Fires after the field has been marked as invalid.
37179              * @param {Roo.form.Field} this
37180              * @param {String} msg The validation message
37181              */
37182             invalid : true,
37183             /**
37184              * @event valid
37185              * Fires after the field has been validated with no errors.
37186              * @param {Roo.form.Field} this
37187              */
37188             valid : true,
37189              /**
37190              * @event keyup
37191              * Fires after the key up
37192              * @param {Roo.form.Field} this
37193              * @param {Roo.EventObject}  e The event Object
37194              */
37195             keyup : true
37196         });
37197     },
37198
37199     /**
37200      * Returns the name attribute of the field if available
37201      * @return {String} name The field name
37202      */
37203     getName: function(){
37204          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37205     },
37206
37207     // private
37208     onRender : function(ct, position){
37209         Roo.form.Field.superclass.onRender.call(this, ct, position);
37210         if(!this.el){
37211             var cfg = this.getAutoCreate();
37212             if(!cfg.name){
37213                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37214             }
37215             if (!cfg.name.length) {
37216                 delete cfg.name;
37217             }
37218             if(this.inputType){
37219                 cfg.type = this.inputType;
37220             }
37221             this.el = ct.createChild(cfg, position);
37222         }
37223         var type = this.el.dom.type;
37224         if(type){
37225             if(type == 'password'){
37226                 type = 'text';
37227             }
37228             this.el.addClass('x-form-'+type);
37229         }
37230         if(this.readOnly){
37231             this.el.dom.readOnly = true;
37232         }
37233         if(this.tabIndex !== undefined){
37234             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37235         }
37236
37237         this.el.addClass([this.fieldClass, this.cls]);
37238         this.initValue();
37239     },
37240
37241     /**
37242      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37243      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37244      * @return {Roo.form.Field} this
37245      */
37246     applyTo : function(target){
37247         this.allowDomMove = false;
37248         this.el = Roo.get(target);
37249         this.render(this.el.dom.parentNode);
37250         return this;
37251     },
37252
37253     // private
37254     initValue : function(){
37255         if(this.value !== undefined){
37256             this.setValue(this.value);
37257         }else if(this.el.dom.value.length > 0){
37258             this.setValue(this.el.dom.value);
37259         }
37260     },
37261
37262     /**
37263      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37264      */
37265     isDirty : function() {
37266         if(this.disabled) {
37267             return false;
37268         }
37269         return String(this.getValue()) !== String(this.originalValue);
37270     },
37271
37272     // private
37273     afterRender : function(){
37274         Roo.form.Field.superclass.afterRender.call(this);
37275         this.initEvents();
37276     },
37277
37278     // private
37279     fireKey : function(e){
37280         //Roo.log('field ' + e.getKey());
37281         if(e.isNavKeyPress()){
37282             this.fireEvent("specialkey", this, e);
37283         }
37284     },
37285
37286     /**
37287      * Resets the current field value to the originally loaded value and clears any validation messages
37288      */
37289     reset : function(){
37290         this.setValue(this.resetValue);
37291         this.clearInvalid();
37292     },
37293
37294     // private
37295     initEvents : function(){
37296         // safari killled keypress - so keydown is now used..
37297         this.el.on("keydown" , this.fireKey,  this);
37298         this.el.on("focus", this.onFocus,  this);
37299         this.el.on("blur", this.onBlur,  this);
37300         this.el.relayEvent('keyup', this);
37301
37302         // reference to original value for reset
37303         this.originalValue = this.getValue();
37304         this.resetValue =  this.getValue();
37305     },
37306
37307     // private
37308     onFocus : function(){
37309         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37310             this.el.addClass(this.focusClass);
37311         }
37312         if(!this.hasFocus){
37313             this.hasFocus = true;
37314             this.startValue = this.getValue();
37315             this.fireEvent("focus", this);
37316         }
37317     },
37318
37319     beforeBlur : Roo.emptyFn,
37320
37321     // private
37322     onBlur : function(){
37323         this.beforeBlur();
37324         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37325             this.el.removeClass(this.focusClass);
37326         }
37327         this.hasFocus = false;
37328         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37329             this.validate();
37330         }
37331         var v = this.getValue();
37332         if(String(v) !== String(this.startValue)){
37333             this.fireEvent('change', this, v, this.startValue);
37334         }
37335         this.fireEvent("blur", this);
37336     },
37337
37338     /**
37339      * Returns whether or not the field value is currently valid
37340      * @param {Boolean} preventMark True to disable marking the field invalid
37341      * @return {Boolean} True if the value is valid, else false
37342      */
37343     isValid : function(preventMark){
37344         if(this.disabled){
37345             return true;
37346         }
37347         var restore = this.preventMark;
37348         this.preventMark = preventMark === true;
37349         var v = this.validateValue(this.processValue(this.getRawValue()));
37350         this.preventMark = restore;
37351         return v;
37352     },
37353
37354     /**
37355      * Validates the field value
37356      * @return {Boolean} True if the value is valid, else false
37357      */
37358     validate : function(){
37359         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37360             this.clearInvalid();
37361             return true;
37362         }
37363         return false;
37364     },
37365
37366     processValue : function(value){
37367         return value;
37368     },
37369
37370     // private
37371     // Subclasses should provide the validation implementation by overriding this
37372     validateValue : function(value){
37373         return true;
37374     },
37375
37376     /**
37377      * Mark this field as invalid
37378      * @param {String} msg The validation message
37379      */
37380     markInvalid : function(msg){
37381         if(!this.rendered || this.preventMark){ // not rendered
37382             return;
37383         }
37384         
37385         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37386         
37387         obj.el.addClass(this.invalidClass);
37388         msg = msg || this.invalidText;
37389         switch(this.msgTarget){
37390             case 'qtip':
37391                 obj.el.dom.qtip = msg;
37392                 obj.el.dom.qclass = 'x-form-invalid-tip';
37393                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37394                     Roo.QuickTips.enable();
37395                 }
37396                 break;
37397             case 'title':
37398                 this.el.dom.title = msg;
37399                 break;
37400             case 'under':
37401                 if(!this.errorEl){
37402                     var elp = this.el.findParent('.x-form-element', 5, true);
37403                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37404                     this.errorEl.setWidth(elp.getWidth(true)-20);
37405                 }
37406                 this.errorEl.update(msg);
37407                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37408                 break;
37409             case 'side':
37410                 if(!this.errorIcon){
37411                     var elp = this.el.findParent('.x-form-element', 5, true);
37412                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37413                 }
37414                 this.alignErrorIcon();
37415                 this.errorIcon.dom.qtip = msg;
37416                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37417                 this.errorIcon.show();
37418                 this.on('resize', this.alignErrorIcon, this);
37419                 break;
37420             default:
37421                 var t = Roo.getDom(this.msgTarget);
37422                 t.innerHTML = msg;
37423                 t.style.display = this.msgDisplay;
37424                 break;
37425         }
37426         this.fireEvent('invalid', this, msg);
37427     },
37428
37429     // private
37430     alignErrorIcon : function(){
37431         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37432     },
37433
37434     /**
37435      * Clear any invalid styles/messages for this field
37436      */
37437     clearInvalid : function(){
37438         if(!this.rendered || this.preventMark){ // not rendered
37439             return;
37440         }
37441         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37442         
37443         obj.el.removeClass(this.invalidClass);
37444         switch(this.msgTarget){
37445             case 'qtip':
37446                 obj.el.dom.qtip = '';
37447                 break;
37448             case 'title':
37449                 this.el.dom.title = '';
37450                 break;
37451             case 'under':
37452                 if(this.errorEl){
37453                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37454                 }
37455                 break;
37456             case 'side':
37457                 if(this.errorIcon){
37458                     this.errorIcon.dom.qtip = '';
37459                     this.errorIcon.hide();
37460                     this.un('resize', this.alignErrorIcon, this);
37461                 }
37462                 break;
37463             default:
37464                 var t = Roo.getDom(this.msgTarget);
37465                 t.innerHTML = '';
37466                 t.style.display = 'none';
37467                 break;
37468         }
37469         this.fireEvent('valid', this);
37470     },
37471
37472     /**
37473      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37474      * @return {Mixed} value The field value
37475      */
37476     getRawValue : function(){
37477         var v = this.el.getValue();
37478         
37479         return v;
37480     },
37481
37482     /**
37483      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37484      * @return {Mixed} value The field value
37485      */
37486     getValue : function(){
37487         var v = this.el.getValue();
37488          
37489         return v;
37490     },
37491
37492     /**
37493      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37494      * @param {Mixed} value The value to set
37495      */
37496     setRawValue : function(v){
37497         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37498     },
37499
37500     /**
37501      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37502      * @param {Mixed} value The value to set
37503      */
37504     setValue : function(v){
37505         this.value = v;
37506         if(this.rendered){
37507             this.el.dom.value = (v === null || v === undefined ? '' : v);
37508              this.validate();
37509         }
37510     },
37511
37512     adjustSize : function(w, h){
37513         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37514         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37515         return s;
37516     },
37517
37518     adjustWidth : function(tag, w){
37519         tag = tag.toLowerCase();
37520         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37521             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37522                 if(tag == 'input'){
37523                     return w + 2;
37524                 }
37525                 if(tag == 'textarea'){
37526                     return w-2;
37527                 }
37528             }else if(Roo.isOpera){
37529                 if(tag == 'input'){
37530                     return w + 2;
37531                 }
37532                 if(tag == 'textarea'){
37533                     return w-2;
37534                 }
37535             }
37536         }
37537         return w;
37538     }
37539 });
37540
37541
37542 // anything other than normal should be considered experimental
37543 Roo.form.Field.msgFx = {
37544     normal : {
37545         show: function(msgEl, f){
37546             msgEl.setDisplayed('block');
37547         },
37548
37549         hide : function(msgEl, f){
37550             msgEl.setDisplayed(false).update('');
37551         }
37552     },
37553
37554     slide : {
37555         show: function(msgEl, f){
37556             msgEl.slideIn('t', {stopFx:true});
37557         },
37558
37559         hide : function(msgEl, f){
37560             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37561         }
37562     },
37563
37564     slideRight : {
37565         show: function(msgEl, f){
37566             msgEl.fixDisplay();
37567             msgEl.alignTo(f.el, 'tl-tr');
37568             msgEl.slideIn('l', {stopFx:true});
37569         },
37570
37571         hide : function(msgEl, f){
37572             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37573         }
37574     }
37575 };/*
37576  * Based on:
37577  * Ext JS Library 1.1.1
37578  * Copyright(c) 2006-2007, Ext JS, LLC.
37579  *
37580  * Originally Released Under LGPL - original licence link has changed is not relivant.
37581  *
37582  * Fork - LGPL
37583  * <script type="text/javascript">
37584  */
37585  
37586
37587 /**
37588  * @class Roo.form.TextField
37589  * @extends Roo.form.Field
37590  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37591  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37592  * @constructor
37593  * Creates a new TextField
37594  * @param {Object} config Configuration options
37595  */
37596 Roo.form.TextField = function(config){
37597     Roo.form.TextField.superclass.constructor.call(this, config);
37598     this.addEvents({
37599         /**
37600          * @event autosize
37601          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37602          * according to the default logic, but this event provides a hook for the developer to apply additional
37603          * logic at runtime to resize the field if needed.
37604              * @param {Roo.form.Field} this This text field
37605              * @param {Number} width The new field width
37606              */
37607         autosize : true
37608     });
37609 };
37610
37611 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37612     /**
37613      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37614      */
37615     grow : false,
37616     /**
37617      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37618      */
37619     growMin : 30,
37620     /**
37621      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37622      */
37623     growMax : 800,
37624     /**
37625      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37626      */
37627     vtype : null,
37628     /**
37629      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37630      */
37631     maskRe : null,
37632     /**
37633      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37634      */
37635     disableKeyFilter : false,
37636     /**
37637      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37638      */
37639     allowBlank : true,
37640     /**
37641      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37642      */
37643     minLength : 0,
37644     /**
37645      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37646      */
37647     maxLength : Number.MAX_VALUE,
37648     /**
37649      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37650      */
37651     minLengthText : "The minimum length for this field is {0}",
37652     /**
37653      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37654      */
37655     maxLengthText : "The maximum length for this field is {0}",
37656     /**
37657      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37658      */
37659     selectOnFocus : false,
37660     /**
37661      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37662      */
37663     blankText : "This field is required",
37664     /**
37665      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37666      * If available, this function will be called only after the basic validators all return true, and will be passed the
37667      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37668      */
37669     validator : null,
37670     /**
37671      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37672      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37673      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37674      */
37675     regex : null,
37676     /**
37677      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37678      */
37679     regexText : "",
37680     /**
37681      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37682      */
37683     emptyText : null,
37684    
37685
37686     // private
37687     initEvents : function()
37688     {
37689         if (this.emptyText) {
37690             this.el.attr('placeholder', this.emptyText);
37691         }
37692         
37693         Roo.form.TextField.superclass.initEvents.call(this);
37694         if(this.validationEvent == 'keyup'){
37695             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37696             this.el.on('keyup', this.filterValidation, this);
37697         }
37698         else if(this.validationEvent !== false){
37699             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37700         }
37701         
37702         if(this.selectOnFocus){
37703             this.on("focus", this.preFocus, this);
37704             
37705         }
37706         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37707             this.el.on("keypress", this.filterKeys, this);
37708         }
37709         if(this.grow){
37710             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37711             this.el.on("click", this.autoSize,  this);
37712         }
37713         if(this.el.is('input[type=password]') && Roo.isSafari){
37714             this.el.on('keydown', this.SafariOnKeyDown, this);
37715         }
37716     },
37717
37718     processValue : function(value){
37719         if(this.stripCharsRe){
37720             var newValue = value.replace(this.stripCharsRe, '');
37721             if(newValue !== value){
37722                 this.setRawValue(newValue);
37723                 return newValue;
37724             }
37725         }
37726         return value;
37727     },
37728
37729     filterValidation : function(e){
37730         if(!e.isNavKeyPress()){
37731             this.validationTask.delay(this.validationDelay);
37732         }
37733     },
37734
37735     // private
37736     onKeyUp : function(e){
37737         if(!e.isNavKeyPress()){
37738             this.autoSize();
37739         }
37740     },
37741
37742     /**
37743      * Resets the current field value to the originally-loaded value and clears any validation messages.
37744      *  
37745      */
37746     reset : function(){
37747         Roo.form.TextField.superclass.reset.call(this);
37748        
37749     },
37750
37751     
37752     // private
37753     preFocus : function(){
37754         
37755         if(this.selectOnFocus){
37756             this.el.dom.select();
37757         }
37758     },
37759
37760     
37761     // private
37762     filterKeys : function(e){
37763         var k = e.getKey();
37764         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37765             return;
37766         }
37767         var c = e.getCharCode(), cc = String.fromCharCode(c);
37768         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37769             return;
37770         }
37771         if(!this.maskRe.test(cc)){
37772             e.stopEvent();
37773         }
37774     },
37775
37776     setValue : function(v){
37777         
37778         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37779         
37780         this.autoSize();
37781     },
37782
37783     /**
37784      * Validates a value according to the field's validation rules and marks the field as invalid
37785      * if the validation fails
37786      * @param {Mixed} value The value to validate
37787      * @return {Boolean} True if the value is valid, else false
37788      */
37789     validateValue : function(value){
37790         if(value.length < 1)  { // if it's blank
37791              if(this.allowBlank){
37792                 this.clearInvalid();
37793                 return true;
37794              }else{
37795                 this.markInvalid(this.blankText);
37796                 return false;
37797              }
37798         }
37799         if(value.length < this.minLength){
37800             this.markInvalid(String.format(this.minLengthText, this.minLength));
37801             return false;
37802         }
37803         if(value.length > this.maxLength){
37804             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37805             return false;
37806         }
37807         if(this.vtype){
37808             var vt = Roo.form.VTypes;
37809             if(!vt[this.vtype](value, this)){
37810                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37811                 return false;
37812             }
37813         }
37814         if(typeof this.validator == "function"){
37815             var msg = this.validator(value);
37816             if(msg !== true){
37817                 this.markInvalid(msg);
37818                 return false;
37819             }
37820         }
37821         if(this.regex && !this.regex.test(value)){
37822             this.markInvalid(this.regexText);
37823             return false;
37824         }
37825         return true;
37826     },
37827
37828     /**
37829      * Selects text in this field
37830      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37831      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37832      */
37833     selectText : function(start, end){
37834         var v = this.getRawValue();
37835         if(v.length > 0){
37836             start = start === undefined ? 0 : start;
37837             end = end === undefined ? v.length : end;
37838             var d = this.el.dom;
37839             if(d.setSelectionRange){
37840                 d.setSelectionRange(start, end);
37841             }else if(d.createTextRange){
37842                 var range = d.createTextRange();
37843                 range.moveStart("character", start);
37844                 range.moveEnd("character", v.length-end);
37845                 range.select();
37846             }
37847         }
37848     },
37849
37850     /**
37851      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37852      * This only takes effect if grow = true, and fires the autosize event.
37853      */
37854     autoSize : function(){
37855         if(!this.grow || !this.rendered){
37856             return;
37857         }
37858         if(!this.metrics){
37859             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37860         }
37861         var el = this.el;
37862         var v = el.dom.value;
37863         var d = document.createElement('div');
37864         d.appendChild(document.createTextNode(v));
37865         v = d.innerHTML;
37866         d = null;
37867         v += "&#160;";
37868         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37869         this.el.setWidth(w);
37870         this.fireEvent("autosize", this, w);
37871     },
37872     
37873     // private
37874     SafariOnKeyDown : function(event)
37875     {
37876         // this is a workaround for a password hang bug on chrome/ webkit.
37877         
37878         var isSelectAll = false;
37879         
37880         if(this.el.dom.selectionEnd > 0){
37881             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37882         }
37883         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37884             event.preventDefault();
37885             this.setValue('');
37886             return;
37887         }
37888         
37889         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37890             
37891             event.preventDefault();
37892             // this is very hacky as keydown always get's upper case.
37893             
37894             var cc = String.fromCharCode(event.getCharCode());
37895             
37896             
37897             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37898             
37899         }
37900         
37901         
37902     }
37903 });/*
37904  * Based on:
37905  * Ext JS Library 1.1.1
37906  * Copyright(c) 2006-2007, Ext JS, LLC.
37907  *
37908  * Originally Released Under LGPL - original licence link has changed is not relivant.
37909  *
37910  * Fork - LGPL
37911  * <script type="text/javascript">
37912  */
37913  
37914 /**
37915  * @class Roo.form.Hidden
37916  * @extends Roo.form.TextField
37917  * Simple Hidden element used on forms 
37918  * 
37919  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37920  * 
37921  * @constructor
37922  * Creates a new Hidden form element.
37923  * @param {Object} config Configuration options
37924  */
37925
37926
37927
37928 // easy hidden field...
37929 Roo.form.Hidden = function(config){
37930     Roo.form.Hidden.superclass.constructor.call(this, config);
37931 };
37932   
37933 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37934     fieldLabel:      '',
37935     inputType:      'hidden',
37936     width:          50,
37937     allowBlank:     true,
37938     labelSeparator: '',
37939     hidden:         true,
37940     itemCls :       'x-form-item-display-none'
37941
37942
37943 });
37944
37945
37946 /*
37947  * Based on:
37948  * Ext JS Library 1.1.1
37949  * Copyright(c) 2006-2007, Ext JS, LLC.
37950  *
37951  * Originally Released Under LGPL - original licence link has changed is not relivant.
37952  *
37953  * Fork - LGPL
37954  * <script type="text/javascript">
37955  */
37956  
37957 /**
37958  * @class Roo.form.TriggerField
37959  * @extends Roo.form.TextField
37960  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37961  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37962  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37963  * for which you can provide a custom implementation.  For example:
37964  * <pre><code>
37965 var trigger = new Roo.form.TriggerField();
37966 trigger.onTriggerClick = myTriggerFn;
37967 trigger.applyTo('my-field');
37968 </code></pre>
37969  *
37970  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37971  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37972  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37973  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37974  * @constructor
37975  * Create a new TriggerField.
37976  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37977  * to the base TextField)
37978  */
37979 Roo.form.TriggerField = function(config){
37980     this.mimicing = false;
37981     Roo.form.TriggerField.superclass.constructor.call(this, config);
37982 };
37983
37984 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37985     /**
37986      * @cfg {String} triggerClass A CSS class to apply to the trigger
37987      */
37988     /**
37989      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37990      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37991      */
37992     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
37993     /**
37994      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37995      */
37996     hideTrigger:false,
37997
37998     /** @cfg {Boolean} grow @hide */
37999     /** @cfg {Number} growMin @hide */
38000     /** @cfg {Number} growMax @hide */
38001
38002     /**
38003      * @hide 
38004      * @method
38005      */
38006     autoSize: Roo.emptyFn,
38007     // private
38008     monitorTab : true,
38009     // private
38010     deferHeight : true,
38011
38012     
38013     actionMode : 'wrap',
38014     // private
38015     onResize : function(w, h){
38016         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38017         if(typeof w == 'number'){
38018             var x = w - this.trigger.getWidth();
38019             this.el.setWidth(this.adjustWidth('input', x));
38020             this.trigger.setStyle('left', x+'px');
38021         }
38022     },
38023
38024     // private
38025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38026
38027     // private
38028     getResizeEl : function(){
38029         return this.wrap;
38030     },
38031
38032     // private
38033     getPositionEl : function(){
38034         return this.wrap;
38035     },
38036
38037     // private
38038     alignErrorIcon : function(){
38039         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38040     },
38041
38042     // private
38043     onRender : function(ct, position){
38044         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38045         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38046         this.trigger = this.wrap.createChild(this.triggerConfig ||
38047                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38048         if(this.hideTrigger){
38049             this.trigger.setDisplayed(false);
38050         }
38051         this.initTrigger();
38052         if(!this.width){
38053             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38054         }
38055     },
38056
38057     // private
38058     initTrigger : function(){
38059         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38060         this.trigger.addClassOnOver('x-form-trigger-over');
38061         this.trigger.addClassOnClick('x-form-trigger-click');
38062     },
38063
38064     // private
38065     onDestroy : function(){
38066         if(this.trigger){
38067             this.trigger.removeAllListeners();
38068             this.trigger.remove();
38069         }
38070         if(this.wrap){
38071             this.wrap.remove();
38072         }
38073         Roo.form.TriggerField.superclass.onDestroy.call(this);
38074     },
38075
38076     // private
38077     onFocus : function(){
38078         Roo.form.TriggerField.superclass.onFocus.call(this);
38079         if(!this.mimicing){
38080             this.wrap.addClass('x-trigger-wrap-focus');
38081             this.mimicing = true;
38082             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38083             if(this.monitorTab){
38084                 this.el.on("keydown", this.checkTab, this);
38085             }
38086         }
38087     },
38088
38089     // private
38090     checkTab : function(e){
38091         if(e.getKey() == e.TAB){
38092             this.triggerBlur();
38093         }
38094     },
38095
38096     // private
38097     onBlur : function(){
38098         // do nothing
38099     },
38100
38101     // private
38102     mimicBlur : function(e, t){
38103         if(!this.wrap.contains(t) && this.validateBlur()){
38104             this.triggerBlur();
38105         }
38106     },
38107
38108     // private
38109     triggerBlur : function(){
38110         this.mimicing = false;
38111         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38112         if(this.monitorTab){
38113             this.el.un("keydown", this.checkTab, this);
38114         }
38115         this.wrap.removeClass('x-trigger-wrap-focus');
38116         Roo.form.TriggerField.superclass.onBlur.call(this);
38117     },
38118
38119     // private
38120     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38121     validateBlur : function(e, t){
38122         return true;
38123     },
38124
38125     // private
38126     onDisable : function(){
38127         Roo.form.TriggerField.superclass.onDisable.call(this);
38128         if(this.wrap){
38129             this.wrap.addClass('x-item-disabled');
38130         }
38131     },
38132
38133     // private
38134     onEnable : function(){
38135         Roo.form.TriggerField.superclass.onEnable.call(this);
38136         if(this.wrap){
38137             this.wrap.removeClass('x-item-disabled');
38138         }
38139     },
38140
38141     // private
38142     onShow : function(){
38143         var ae = this.getActionEl();
38144         
38145         if(ae){
38146             ae.dom.style.display = '';
38147             ae.dom.style.visibility = 'visible';
38148         }
38149     },
38150
38151     // private
38152     
38153     onHide : function(){
38154         var ae = this.getActionEl();
38155         ae.dom.style.display = 'none';
38156     },
38157
38158     /**
38159      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38160      * by an implementing function.
38161      * @method
38162      * @param {EventObject} e
38163      */
38164     onTriggerClick : Roo.emptyFn
38165 });
38166
38167 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38168 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38169 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38170 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38171     initComponent : function(){
38172         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38173
38174         this.triggerConfig = {
38175             tag:'span', cls:'x-form-twin-triggers', cn:[
38176             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38177             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38178         ]};
38179     },
38180
38181     getTrigger : function(index){
38182         return this.triggers[index];
38183     },
38184
38185     initTrigger : function(){
38186         var ts = this.trigger.select('.x-form-trigger', true);
38187         this.wrap.setStyle('overflow', 'hidden');
38188         var triggerField = this;
38189         ts.each(function(t, all, index){
38190             t.hide = function(){
38191                 var w = triggerField.wrap.getWidth();
38192                 this.dom.style.display = 'none';
38193                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38194             };
38195             t.show = function(){
38196                 var w = triggerField.wrap.getWidth();
38197                 this.dom.style.display = '';
38198                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38199             };
38200             var triggerIndex = 'Trigger'+(index+1);
38201
38202             if(this['hide'+triggerIndex]){
38203                 t.dom.style.display = 'none';
38204             }
38205             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38206             t.addClassOnOver('x-form-trigger-over');
38207             t.addClassOnClick('x-form-trigger-click');
38208         }, this);
38209         this.triggers = ts.elements;
38210     },
38211
38212     onTrigger1Click : Roo.emptyFn,
38213     onTrigger2Click : Roo.emptyFn
38214 });/*
38215  * Based on:
38216  * Ext JS Library 1.1.1
38217  * Copyright(c) 2006-2007, Ext JS, LLC.
38218  *
38219  * Originally Released Under LGPL - original licence link has changed is not relivant.
38220  *
38221  * Fork - LGPL
38222  * <script type="text/javascript">
38223  */
38224  
38225 /**
38226  * @class Roo.form.TextArea
38227  * @extends Roo.form.TextField
38228  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38229  * support for auto-sizing.
38230  * @constructor
38231  * Creates a new TextArea
38232  * @param {Object} config Configuration options
38233  */
38234 Roo.form.TextArea = function(config){
38235     Roo.form.TextArea.superclass.constructor.call(this, config);
38236     // these are provided exchanges for backwards compat
38237     // minHeight/maxHeight were replaced by growMin/growMax to be
38238     // compatible with TextField growing config values
38239     if(this.minHeight !== undefined){
38240         this.growMin = this.minHeight;
38241     }
38242     if(this.maxHeight !== undefined){
38243         this.growMax = this.maxHeight;
38244     }
38245 };
38246
38247 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38248     /**
38249      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38250      */
38251     growMin : 60,
38252     /**
38253      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38254      */
38255     growMax: 1000,
38256     /**
38257      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38258      * in the field (equivalent to setting overflow: hidden, defaults to false)
38259      */
38260     preventScrollbars: false,
38261     /**
38262      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38263      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38264      */
38265
38266     // private
38267     onRender : function(ct, position){
38268         if(!this.el){
38269             this.defaultAutoCreate = {
38270                 tag: "textarea",
38271                 style:"width:300px;height:60px;",
38272                 autocomplete: "new-password"
38273             };
38274         }
38275         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38276         if(this.grow){
38277             this.textSizeEl = Roo.DomHelper.append(document.body, {
38278                 tag: "pre", cls: "x-form-grow-sizer"
38279             });
38280             if(this.preventScrollbars){
38281                 this.el.setStyle("overflow", "hidden");
38282             }
38283             this.el.setHeight(this.growMin);
38284         }
38285     },
38286
38287     onDestroy : function(){
38288         if(this.textSizeEl){
38289             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38290         }
38291         Roo.form.TextArea.superclass.onDestroy.call(this);
38292     },
38293
38294     // private
38295     onKeyUp : function(e){
38296         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38297             this.autoSize();
38298         }
38299     },
38300
38301     /**
38302      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38303      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38304      */
38305     autoSize : function(){
38306         if(!this.grow || !this.textSizeEl){
38307             return;
38308         }
38309         var el = this.el;
38310         var v = el.dom.value;
38311         var ts = this.textSizeEl;
38312
38313         ts.innerHTML = '';
38314         ts.appendChild(document.createTextNode(v));
38315         v = ts.innerHTML;
38316
38317         Roo.fly(ts).setWidth(this.el.getWidth());
38318         if(v.length < 1){
38319             v = "&#160;&#160;";
38320         }else{
38321             if(Roo.isIE){
38322                 v = v.replace(/\n/g, '<p>&#160;</p>');
38323             }
38324             v += "&#160;\n&#160;";
38325         }
38326         ts.innerHTML = v;
38327         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38328         if(h != this.lastHeight){
38329             this.lastHeight = h;
38330             this.el.setHeight(h);
38331             this.fireEvent("autosize", this, h);
38332         }
38333     }
38334 });/*
38335  * Based on:
38336  * Ext JS Library 1.1.1
38337  * Copyright(c) 2006-2007, Ext JS, LLC.
38338  *
38339  * Originally Released Under LGPL - original licence link has changed is not relivant.
38340  *
38341  * Fork - LGPL
38342  * <script type="text/javascript">
38343  */
38344  
38345
38346 /**
38347  * @class Roo.form.NumberField
38348  * @extends Roo.form.TextField
38349  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38350  * @constructor
38351  * Creates a new NumberField
38352  * @param {Object} config Configuration options
38353  */
38354 Roo.form.NumberField = function(config){
38355     Roo.form.NumberField.superclass.constructor.call(this, config);
38356 };
38357
38358 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38359     /**
38360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38361      */
38362     fieldClass: "x-form-field x-form-num-field",
38363     /**
38364      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38365      */
38366     allowDecimals : true,
38367     /**
38368      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38369      */
38370     decimalSeparator : ".",
38371     /**
38372      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38373      */
38374     decimalPrecision : 2,
38375     /**
38376      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38377      */
38378     allowNegative : true,
38379     /**
38380      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38381      */
38382     minValue : Number.NEGATIVE_INFINITY,
38383     /**
38384      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38385      */
38386     maxValue : Number.MAX_VALUE,
38387     /**
38388      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38389      */
38390     minText : "The minimum value for this field is {0}",
38391     /**
38392      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38393      */
38394     maxText : "The maximum value for this field is {0}",
38395     /**
38396      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38397      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38398      */
38399     nanText : "{0} is not a valid number",
38400
38401     // private
38402     initEvents : function(){
38403         Roo.form.NumberField.superclass.initEvents.call(this);
38404         var allowed = "0123456789";
38405         if(this.allowDecimals){
38406             allowed += this.decimalSeparator;
38407         }
38408         if(this.allowNegative){
38409             allowed += "-";
38410         }
38411         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38412         var keyPress = function(e){
38413             var k = e.getKey();
38414             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38415                 return;
38416             }
38417             var c = e.getCharCode();
38418             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38419                 e.stopEvent();
38420             }
38421         };
38422         this.el.on("keypress", keyPress, this);
38423     },
38424
38425     // private
38426     validateValue : function(value){
38427         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38428             return false;
38429         }
38430         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38431              return true;
38432         }
38433         var num = this.parseValue(value);
38434         if(isNaN(num)){
38435             this.markInvalid(String.format(this.nanText, value));
38436             return false;
38437         }
38438         if(num < this.minValue){
38439             this.markInvalid(String.format(this.minText, this.minValue));
38440             return false;
38441         }
38442         if(num > this.maxValue){
38443             this.markInvalid(String.format(this.maxText, this.maxValue));
38444             return false;
38445         }
38446         return true;
38447     },
38448
38449     getValue : function(){
38450         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38451     },
38452
38453     // private
38454     parseValue : function(value){
38455         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38456         return isNaN(value) ? '' : value;
38457     },
38458
38459     // private
38460     fixPrecision : function(value){
38461         var nan = isNaN(value);
38462         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38463             return nan ? '' : value;
38464         }
38465         return parseFloat(value).toFixed(this.decimalPrecision);
38466     },
38467
38468     setValue : function(v){
38469         v = this.fixPrecision(v);
38470         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38471     },
38472
38473     // private
38474     decimalPrecisionFcn : function(v){
38475         return Math.floor(v);
38476     },
38477
38478     beforeBlur : function(){
38479         var v = this.parseValue(this.getRawValue());
38480         if(v){
38481             this.setValue(v);
38482         }
38483     }
38484 });/*
38485  * Based on:
38486  * Ext JS Library 1.1.1
38487  * Copyright(c) 2006-2007, Ext JS, LLC.
38488  *
38489  * Originally Released Under LGPL - original licence link has changed is not relivant.
38490  *
38491  * Fork - LGPL
38492  * <script type="text/javascript">
38493  */
38494  
38495 /**
38496  * @class Roo.form.DateField
38497  * @extends Roo.form.TriggerField
38498  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38499 * @constructor
38500 * Create a new DateField
38501 * @param {Object} config
38502  */
38503 Roo.form.DateField = function(config){
38504     Roo.form.DateField.superclass.constructor.call(this, config);
38505     
38506       this.addEvents({
38507          
38508         /**
38509          * @event select
38510          * Fires when a date is selected
38511              * @param {Roo.form.DateField} combo This combo box
38512              * @param {Date} date The date selected
38513              */
38514         'select' : true
38515          
38516     });
38517     
38518     
38519     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38520     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38521     this.ddMatch = null;
38522     if(this.disabledDates){
38523         var dd = this.disabledDates;
38524         var re = "(?:";
38525         for(var i = 0; i < dd.length; i++){
38526             re += dd[i];
38527             if(i != dd.length-1) re += "|";
38528         }
38529         this.ddMatch = new RegExp(re + ")");
38530     }
38531 };
38532
38533 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38534     /**
38535      * @cfg {String} format
38536      * The default date format string which can be overriden for localization support.  The format must be
38537      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38538      */
38539     format : "m/d/y",
38540     /**
38541      * @cfg {String} altFormats
38542      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38543      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38544      */
38545     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38546     /**
38547      * @cfg {Array} disabledDays
38548      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38549      */
38550     disabledDays : null,
38551     /**
38552      * @cfg {String} disabledDaysText
38553      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38554      */
38555     disabledDaysText : "Disabled",
38556     /**
38557      * @cfg {Array} disabledDates
38558      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38559      * expression so they are very powerful. Some examples:
38560      * <ul>
38561      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38562      * <li>["03/08", "09/16"] would disable those days for every year</li>
38563      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38564      * <li>["03/../2006"] would disable every day in March 2006</li>
38565      * <li>["^03"] would disable every day in every March</li>
38566      * </ul>
38567      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38568      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38569      */
38570     disabledDates : null,
38571     /**
38572      * @cfg {String} disabledDatesText
38573      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38574      */
38575     disabledDatesText : "Disabled",
38576     /**
38577      * @cfg {Date/String} minValue
38578      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38579      * valid format (defaults to null).
38580      */
38581     minValue : null,
38582     /**
38583      * @cfg {Date/String} maxValue
38584      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38585      * valid format (defaults to null).
38586      */
38587     maxValue : null,
38588     /**
38589      * @cfg {String} minText
38590      * The error text to display when the date in the cell is before minValue (defaults to
38591      * 'The date in this field must be after {minValue}').
38592      */
38593     minText : "The date in this field must be equal to or after {0}",
38594     /**
38595      * @cfg {String} maxText
38596      * The error text to display when the date in the cell is after maxValue (defaults to
38597      * 'The date in this field must be before {maxValue}').
38598      */
38599     maxText : "The date in this field must be equal to or before {0}",
38600     /**
38601      * @cfg {String} invalidText
38602      * The error text to display when the date in the field is invalid (defaults to
38603      * '{value} is not a valid date - it must be in the format {format}').
38604      */
38605     invalidText : "{0} is not a valid date - it must be in the format {1}",
38606     /**
38607      * @cfg {String} triggerClass
38608      * An additional CSS class used to style the trigger button.  The trigger will always get the
38609      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38610      * which displays a calendar icon).
38611      */
38612     triggerClass : 'x-form-date-trigger',
38613     
38614
38615     /**
38616      * @cfg {Boolean} useIso
38617      * if enabled, then the date field will use a hidden field to store the 
38618      * real value as iso formated date. default (false)
38619      */ 
38620     useIso : false,
38621     /**
38622      * @cfg {String/Object} autoCreate
38623      * A DomHelper element spec, or true for a default element spec (defaults to
38624      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38625      */ 
38626     // private
38627     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38628     
38629     // private
38630     hiddenField: false,
38631     
38632     onRender : function(ct, position)
38633     {
38634         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38635         if (this.useIso) {
38636             //this.el.dom.removeAttribute('name'); 
38637             Roo.log("Changing name?");
38638             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38639             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38640                     'before', true);
38641             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38642             // prevent input submission
38643             this.hiddenName = this.name;
38644         }
38645             
38646             
38647     },
38648     
38649     // private
38650     validateValue : function(value)
38651     {
38652         value = this.formatDate(value);
38653         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38654             Roo.log('super failed');
38655             return false;
38656         }
38657         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38658              return true;
38659         }
38660         var svalue = value;
38661         value = this.parseDate(value);
38662         if(!value){
38663             Roo.log('parse date failed' + svalue);
38664             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38665             return false;
38666         }
38667         var time = value.getTime();
38668         if(this.minValue && time < this.minValue.getTime()){
38669             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38670             return false;
38671         }
38672         if(this.maxValue && time > this.maxValue.getTime()){
38673             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38674             return false;
38675         }
38676         if(this.disabledDays){
38677             var day = value.getDay();
38678             for(var i = 0; i < this.disabledDays.length; i++) {
38679                 if(day === this.disabledDays[i]){
38680                     this.markInvalid(this.disabledDaysText);
38681                     return false;
38682                 }
38683             }
38684         }
38685         var fvalue = this.formatDate(value);
38686         if(this.ddMatch && this.ddMatch.test(fvalue)){
38687             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38688             return false;
38689         }
38690         return true;
38691     },
38692
38693     // private
38694     // Provides logic to override the default TriggerField.validateBlur which just returns true
38695     validateBlur : function(){
38696         return !this.menu || !this.menu.isVisible();
38697     },
38698     
38699     getName: function()
38700     {
38701         // returns hidden if it's set..
38702         if (!this.rendered) {return ''};
38703         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38704         
38705     },
38706
38707     /**
38708      * Returns the current date value of the date field.
38709      * @return {Date} The date value
38710      */
38711     getValue : function(){
38712         
38713         return  this.hiddenField ?
38714                 this.hiddenField.value :
38715                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38716     },
38717
38718     /**
38719      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38720      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38721      * (the default format used is "m/d/y").
38722      * <br />Usage:
38723      * <pre><code>
38724 //All of these calls set the same date value (May 4, 2006)
38725
38726 //Pass a date object:
38727 var dt = new Date('5/4/06');
38728 dateField.setValue(dt);
38729
38730 //Pass a date string (default format):
38731 dateField.setValue('5/4/06');
38732
38733 //Pass a date string (custom format):
38734 dateField.format = 'Y-m-d';
38735 dateField.setValue('2006-5-4');
38736 </code></pre>
38737      * @param {String/Date} date The date or valid date string
38738      */
38739     setValue : function(date){
38740         if (this.hiddenField) {
38741             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38742         }
38743         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38744         // make sure the value field is always stored as a date..
38745         this.value = this.parseDate(date);
38746         
38747         
38748     },
38749
38750     // private
38751     parseDate : function(value){
38752         if(!value || value instanceof Date){
38753             return value;
38754         }
38755         var v = Date.parseDate(value, this.format);
38756          if (!v && this.useIso) {
38757             v = Date.parseDate(value, 'Y-m-d');
38758         }
38759         if(!v && this.altFormats){
38760             if(!this.altFormatsArray){
38761                 this.altFormatsArray = this.altFormats.split("|");
38762             }
38763             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38764                 v = Date.parseDate(value, this.altFormatsArray[i]);
38765             }
38766         }
38767         return v;
38768     },
38769
38770     // private
38771     formatDate : function(date, fmt){
38772         return (!date || !(date instanceof Date)) ?
38773                date : date.dateFormat(fmt || this.format);
38774     },
38775
38776     // private
38777     menuListeners : {
38778         select: function(m, d){
38779             
38780             this.setValue(d);
38781             this.fireEvent('select', this, d);
38782         },
38783         show : function(){ // retain focus styling
38784             this.onFocus();
38785         },
38786         hide : function(){
38787             this.focus.defer(10, this);
38788             var ml = this.menuListeners;
38789             this.menu.un("select", ml.select,  this);
38790             this.menu.un("show", ml.show,  this);
38791             this.menu.un("hide", ml.hide,  this);
38792         }
38793     },
38794
38795     // private
38796     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38797     onTriggerClick : function(){
38798         if(this.disabled){
38799             return;
38800         }
38801         if(this.menu == null){
38802             this.menu = new Roo.menu.DateMenu();
38803         }
38804         Roo.apply(this.menu.picker,  {
38805             showClear: this.allowBlank,
38806             minDate : this.minValue,
38807             maxDate : this.maxValue,
38808             disabledDatesRE : this.ddMatch,
38809             disabledDatesText : this.disabledDatesText,
38810             disabledDays : this.disabledDays,
38811             disabledDaysText : this.disabledDaysText,
38812             format : this.useIso ? 'Y-m-d' : this.format,
38813             minText : String.format(this.minText, this.formatDate(this.minValue)),
38814             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38815         });
38816         this.menu.on(Roo.apply({}, this.menuListeners, {
38817             scope:this
38818         }));
38819         this.menu.picker.setValue(this.getValue() || new Date());
38820         this.menu.show(this.el, "tl-bl?");
38821     },
38822
38823     beforeBlur : function(){
38824         var v = this.parseDate(this.getRawValue());
38825         if(v){
38826             this.setValue(v);
38827         }
38828     },
38829
38830     /*@
38831      * overide
38832      * 
38833      */
38834     isDirty : function() {
38835         if(this.disabled) {
38836             return false;
38837         }
38838         
38839         if(typeof(this.startValue) === 'undefined'){
38840             return false;
38841         }
38842         
38843         return String(this.getValue()) !== String(this.startValue);
38844         
38845     }
38846 });/*
38847  * Based on:
38848  * Ext JS Library 1.1.1
38849  * Copyright(c) 2006-2007, Ext JS, LLC.
38850  *
38851  * Originally Released Under LGPL - original licence link has changed is not relivant.
38852  *
38853  * Fork - LGPL
38854  * <script type="text/javascript">
38855  */
38856  
38857 /**
38858  * @class Roo.form.MonthField
38859  * @extends Roo.form.TriggerField
38860  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38861 * @constructor
38862 * Create a new MonthField
38863 * @param {Object} config
38864  */
38865 Roo.form.MonthField = function(config){
38866     
38867     Roo.form.MonthField.superclass.constructor.call(this, config);
38868     
38869       this.addEvents({
38870          
38871         /**
38872          * @event select
38873          * Fires when a date is selected
38874              * @param {Roo.form.MonthFieeld} combo This combo box
38875              * @param {Date} date The date selected
38876              */
38877         'select' : true
38878          
38879     });
38880     
38881     
38882     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38883     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38884     this.ddMatch = null;
38885     if(this.disabledDates){
38886         var dd = this.disabledDates;
38887         var re = "(?:";
38888         for(var i = 0; i < dd.length; i++){
38889             re += dd[i];
38890             if(i != dd.length-1) re += "|";
38891         }
38892         this.ddMatch = new RegExp(re + ")");
38893     }
38894 };
38895
38896 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38897     /**
38898      * @cfg {String} format
38899      * The default date format string which can be overriden for localization support.  The format must be
38900      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38901      */
38902     format : "M Y",
38903     /**
38904      * @cfg {String} altFormats
38905      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38906      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38907      */
38908     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38909     /**
38910      * @cfg {Array} disabledDays
38911      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38912      */
38913     disabledDays : [0,1,2,3,4,5,6],
38914     /**
38915      * @cfg {String} disabledDaysText
38916      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38917      */
38918     disabledDaysText : "Disabled",
38919     /**
38920      * @cfg {Array} disabledDates
38921      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38922      * expression so they are very powerful. Some examples:
38923      * <ul>
38924      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38925      * <li>["03/08", "09/16"] would disable those days for every year</li>
38926      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38927      * <li>["03/../2006"] would disable every day in March 2006</li>
38928      * <li>["^03"] would disable every day in every March</li>
38929      * </ul>
38930      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38931      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38932      */
38933     disabledDates : null,
38934     /**
38935      * @cfg {String} disabledDatesText
38936      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38937      */
38938     disabledDatesText : "Disabled",
38939     /**
38940      * @cfg {Date/String} minValue
38941      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38942      * valid format (defaults to null).
38943      */
38944     minValue : null,
38945     /**
38946      * @cfg {Date/String} maxValue
38947      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38948      * valid format (defaults to null).
38949      */
38950     maxValue : null,
38951     /**
38952      * @cfg {String} minText
38953      * The error text to display when the date in the cell is before minValue (defaults to
38954      * 'The date in this field must be after {minValue}').
38955      */
38956     minText : "The date in this field must be equal to or after {0}",
38957     /**
38958      * @cfg {String} maxTextf
38959      * The error text to display when the date in the cell is after maxValue (defaults to
38960      * 'The date in this field must be before {maxValue}').
38961      */
38962     maxText : "The date in this field must be equal to or before {0}",
38963     /**
38964      * @cfg {String} invalidText
38965      * The error text to display when the date in the field is invalid (defaults to
38966      * '{value} is not a valid date - it must be in the format {format}').
38967      */
38968     invalidText : "{0} is not a valid date - it must be in the format {1}",
38969     /**
38970      * @cfg {String} triggerClass
38971      * An additional CSS class used to style the trigger button.  The trigger will always get the
38972      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38973      * which displays a calendar icon).
38974      */
38975     triggerClass : 'x-form-date-trigger',
38976     
38977
38978     /**
38979      * @cfg {Boolean} useIso
38980      * if enabled, then the date field will use a hidden field to store the 
38981      * real value as iso formated date. default (true)
38982      */ 
38983     useIso : true,
38984     /**
38985      * @cfg {String/Object} autoCreate
38986      * A DomHelper element spec, or true for a default element spec (defaults to
38987      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38988      */ 
38989     // private
38990     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38991     
38992     // private
38993     hiddenField: false,
38994     
38995     hideMonthPicker : false,
38996     
38997     onRender : function(ct, position)
38998     {
38999         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39000         if (this.useIso) {
39001             this.el.dom.removeAttribute('name'); 
39002             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39003                     'before', true);
39004             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39005             // prevent input submission
39006             this.hiddenName = this.name;
39007         }
39008             
39009             
39010     },
39011     
39012     // private
39013     validateValue : function(value)
39014     {
39015         value = this.formatDate(value);
39016         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39017             return false;
39018         }
39019         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39020              return true;
39021         }
39022         var svalue = value;
39023         value = this.parseDate(value);
39024         if(!value){
39025             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39026             return false;
39027         }
39028         var time = value.getTime();
39029         if(this.minValue && time < this.minValue.getTime()){
39030             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39031             return false;
39032         }
39033         if(this.maxValue && time > this.maxValue.getTime()){
39034             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39035             return false;
39036         }
39037         /*if(this.disabledDays){
39038             var day = value.getDay();
39039             for(var i = 0; i < this.disabledDays.length; i++) {
39040                 if(day === this.disabledDays[i]){
39041                     this.markInvalid(this.disabledDaysText);
39042                     return false;
39043                 }
39044             }
39045         }
39046         */
39047         var fvalue = this.formatDate(value);
39048         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39049             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39050             return false;
39051         }
39052         */
39053         return true;
39054     },
39055
39056     // private
39057     // Provides logic to override the default TriggerField.validateBlur which just returns true
39058     validateBlur : function(){
39059         return !this.menu || !this.menu.isVisible();
39060     },
39061
39062     /**
39063      * Returns the current date value of the date field.
39064      * @return {Date} The date value
39065      */
39066     getValue : function(){
39067         
39068         
39069         
39070         return  this.hiddenField ?
39071                 this.hiddenField.value :
39072                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39073     },
39074
39075     /**
39076      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39077      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39078      * (the default format used is "m/d/y").
39079      * <br />Usage:
39080      * <pre><code>
39081 //All of these calls set the same date value (May 4, 2006)
39082
39083 //Pass a date object:
39084 var dt = new Date('5/4/06');
39085 monthField.setValue(dt);
39086
39087 //Pass a date string (default format):
39088 monthField.setValue('5/4/06');
39089
39090 //Pass a date string (custom format):
39091 monthField.format = 'Y-m-d';
39092 monthField.setValue('2006-5-4');
39093 </code></pre>
39094      * @param {String/Date} date The date or valid date string
39095      */
39096     setValue : function(date){
39097         Roo.log('month setValue' + date);
39098         // can only be first of month..
39099         
39100         var val = this.parseDate(date);
39101         
39102         if (this.hiddenField) {
39103             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39104         }
39105         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39106         this.value = this.parseDate(date);
39107     },
39108
39109     // private
39110     parseDate : function(value){
39111         if(!value || value instanceof Date){
39112             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39113             return value;
39114         }
39115         var v = Date.parseDate(value, this.format);
39116         if (!v && this.useIso) {
39117             v = Date.parseDate(value, 'Y-m-d');
39118         }
39119         if (v) {
39120             // 
39121             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39122         }
39123         
39124         
39125         if(!v && this.altFormats){
39126             if(!this.altFormatsArray){
39127                 this.altFormatsArray = this.altFormats.split("|");
39128             }
39129             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39130                 v = Date.parseDate(value, this.altFormatsArray[i]);
39131             }
39132         }
39133         return v;
39134     },
39135
39136     // private
39137     formatDate : function(date, fmt){
39138         return (!date || !(date instanceof Date)) ?
39139                date : date.dateFormat(fmt || this.format);
39140     },
39141
39142     // private
39143     menuListeners : {
39144         select: function(m, d){
39145             this.setValue(d);
39146             this.fireEvent('select', this, d);
39147         },
39148         show : function(){ // retain focus styling
39149             this.onFocus();
39150         },
39151         hide : function(){
39152             this.focus.defer(10, this);
39153             var ml = this.menuListeners;
39154             this.menu.un("select", ml.select,  this);
39155             this.menu.un("show", ml.show,  this);
39156             this.menu.un("hide", ml.hide,  this);
39157         }
39158     },
39159     // private
39160     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39161     onTriggerClick : function(){
39162         if(this.disabled){
39163             return;
39164         }
39165         if(this.menu == null){
39166             this.menu = new Roo.menu.DateMenu();
39167            
39168         }
39169         
39170         Roo.apply(this.menu.picker,  {
39171             
39172             showClear: this.allowBlank,
39173             minDate : this.minValue,
39174             maxDate : this.maxValue,
39175             disabledDatesRE : this.ddMatch,
39176             disabledDatesText : this.disabledDatesText,
39177             
39178             format : this.useIso ? 'Y-m-d' : this.format,
39179             minText : String.format(this.minText, this.formatDate(this.minValue)),
39180             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39181             
39182         });
39183          this.menu.on(Roo.apply({}, this.menuListeners, {
39184             scope:this
39185         }));
39186        
39187         
39188         var m = this.menu;
39189         var p = m.picker;
39190         
39191         // hide month picker get's called when we called by 'before hide';
39192         
39193         var ignorehide = true;
39194         p.hideMonthPicker  = function(disableAnim){
39195             if (ignorehide) {
39196                 return;
39197             }
39198              if(this.monthPicker){
39199                 Roo.log("hideMonthPicker called");
39200                 if(disableAnim === true){
39201                     this.monthPicker.hide();
39202                 }else{
39203                     this.monthPicker.slideOut('t', {duration:.2});
39204                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39205                     p.fireEvent("select", this, this.value);
39206                     m.hide();
39207                 }
39208             }
39209         }
39210         
39211         Roo.log('picker set value');
39212         Roo.log(this.getValue());
39213         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39214         m.show(this.el, 'tl-bl?');
39215         ignorehide  = false;
39216         // this will trigger hideMonthPicker..
39217         
39218         
39219         // hidden the day picker
39220         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39221         
39222         
39223         
39224       
39225         
39226         p.showMonthPicker.defer(100, p);
39227     
39228         
39229        
39230     },
39231
39232     beforeBlur : function(){
39233         var v = this.parseDate(this.getRawValue());
39234         if(v){
39235             this.setValue(v);
39236         }
39237     }
39238
39239     /** @cfg {Boolean} grow @hide */
39240     /** @cfg {Number} growMin @hide */
39241     /** @cfg {Number} growMax @hide */
39242     /**
39243      * @hide
39244      * @method autoSize
39245      */
39246 });/*
39247  * Based on:
39248  * Ext JS Library 1.1.1
39249  * Copyright(c) 2006-2007, Ext JS, LLC.
39250  *
39251  * Originally Released Under LGPL - original licence link has changed is not relivant.
39252  *
39253  * Fork - LGPL
39254  * <script type="text/javascript">
39255  */
39256  
39257
39258 /**
39259  * @class Roo.form.ComboBox
39260  * @extends Roo.form.TriggerField
39261  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39262  * @constructor
39263  * Create a new ComboBox.
39264  * @param {Object} config Configuration options
39265  */
39266 Roo.form.ComboBox = function(config){
39267     Roo.form.ComboBox.superclass.constructor.call(this, config);
39268     this.addEvents({
39269         /**
39270          * @event expand
39271          * Fires when the dropdown list is expanded
39272              * @param {Roo.form.ComboBox} combo This combo box
39273              */
39274         'expand' : true,
39275         /**
39276          * @event collapse
39277          * Fires when the dropdown list is collapsed
39278              * @param {Roo.form.ComboBox} combo This combo box
39279              */
39280         'collapse' : true,
39281         /**
39282          * @event beforeselect
39283          * Fires before a list item is selected. Return false to cancel the selection.
39284              * @param {Roo.form.ComboBox} combo This combo box
39285              * @param {Roo.data.Record} record The data record returned from the underlying store
39286              * @param {Number} index The index of the selected item in the dropdown list
39287              */
39288         'beforeselect' : true,
39289         /**
39290          * @event select
39291          * Fires when a list item is selected
39292              * @param {Roo.form.ComboBox} combo This combo box
39293              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39294              * @param {Number} index The index of the selected item in the dropdown list
39295              */
39296         'select' : true,
39297         /**
39298          * @event beforequery
39299          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39300          * The event object passed has these properties:
39301              * @param {Roo.form.ComboBox} combo This combo box
39302              * @param {String} query The query
39303              * @param {Boolean} forceAll true to force "all" query
39304              * @param {Boolean} cancel true to cancel the query
39305              * @param {Object} e The query event object
39306              */
39307         'beforequery': true,
39308          /**
39309          * @event add
39310          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39311              * @param {Roo.form.ComboBox} combo This combo box
39312              */
39313         'add' : true,
39314         /**
39315          * @event edit
39316          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39317              * @param {Roo.form.ComboBox} combo This combo box
39318              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39319              */
39320         'edit' : true
39321         
39322         
39323     });
39324     if(this.transform){
39325         this.allowDomMove = false;
39326         var s = Roo.getDom(this.transform);
39327         if(!this.hiddenName){
39328             this.hiddenName = s.name;
39329         }
39330         if(!this.store){
39331             this.mode = 'local';
39332             var d = [], opts = s.options;
39333             for(var i = 0, len = opts.length;i < len; i++){
39334                 var o = opts[i];
39335                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39336                 if(o.selected) {
39337                     this.value = value;
39338                 }
39339                 d.push([value, o.text]);
39340             }
39341             this.store = new Roo.data.SimpleStore({
39342                 'id': 0,
39343                 fields: ['value', 'text'],
39344                 data : d
39345             });
39346             this.valueField = 'value';
39347             this.displayField = 'text';
39348         }
39349         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39350         if(!this.lazyRender){
39351             this.target = true;
39352             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39353             s.parentNode.removeChild(s); // remove it
39354             this.render(this.el.parentNode);
39355         }else{
39356             s.parentNode.removeChild(s); // remove it
39357         }
39358
39359     }
39360     if (this.store) {
39361         this.store = Roo.factory(this.store, Roo.data);
39362     }
39363     
39364     this.selectedIndex = -1;
39365     if(this.mode == 'local'){
39366         if(config.queryDelay === undefined){
39367             this.queryDelay = 10;
39368         }
39369         if(config.minChars === undefined){
39370             this.minChars = 0;
39371         }
39372     }
39373 };
39374
39375 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39376     /**
39377      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39378      */
39379     /**
39380      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39381      * rendering into an Roo.Editor, defaults to false)
39382      */
39383     /**
39384      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39385      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39386      */
39387     /**
39388      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39389      */
39390     /**
39391      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39392      * the dropdown list (defaults to undefined, with no header element)
39393      */
39394
39395      /**
39396      * @cfg {String/Roo.Template} tpl The template to use to render the output
39397      */
39398      
39399     // private
39400     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39401     /**
39402      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39403      */
39404     listWidth: undefined,
39405     /**
39406      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39407      * mode = 'remote' or 'text' if mode = 'local')
39408      */
39409     displayField: undefined,
39410     /**
39411      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39412      * mode = 'remote' or 'value' if mode = 'local'). 
39413      * Note: use of a valueField requires the user make a selection
39414      * in order for a value to be mapped.
39415      */
39416     valueField: undefined,
39417     
39418     
39419     /**
39420      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39421      * field's data value (defaults to the underlying DOM element's name)
39422      */
39423     hiddenName: undefined,
39424     /**
39425      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39426      */
39427     listClass: '',
39428     /**
39429      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39430      */
39431     selectedClass: 'x-combo-selected',
39432     /**
39433      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39434      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39435      * which displays a downward arrow icon).
39436      */
39437     triggerClass : 'x-form-arrow-trigger',
39438     /**
39439      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39440      */
39441     shadow:'sides',
39442     /**
39443      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39444      * anchor positions (defaults to 'tl-bl')
39445      */
39446     listAlign: 'tl-bl?',
39447     /**
39448      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39449      */
39450     maxHeight: 300,
39451     /**
39452      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39453      * query specified by the allQuery config option (defaults to 'query')
39454      */
39455     triggerAction: 'query',
39456     /**
39457      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39458      * (defaults to 4, does not apply if editable = false)
39459      */
39460     minChars : 4,
39461     /**
39462      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39463      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39464      */
39465     typeAhead: false,
39466     /**
39467      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39468      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39469      */
39470     queryDelay: 500,
39471     /**
39472      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39473      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39474      */
39475     pageSize: 0,
39476     /**
39477      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39478      * when editable = true (defaults to false)
39479      */
39480     selectOnFocus:false,
39481     /**
39482      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39483      */
39484     queryParam: 'query',
39485     /**
39486      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39487      * when mode = 'remote' (defaults to 'Loading...')
39488      */
39489     loadingText: 'Loading...',
39490     /**
39491      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39492      */
39493     resizable: false,
39494     /**
39495      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39496      */
39497     handleHeight : 8,
39498     /**
39499      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39500      * traditional select (defaults to true)
39501      */
39502     editable: true,
39503     /**
39504      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39505      */
39506     allQuery: '',
39507     /**
39508      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39509      */
39510     mode: 'remote',
39511     /**
39512      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39513      * listWidth has a higher value)
39514      */
39515     minListWidth : 70,
39516     /**
39517      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39518      * allow the user to set arbitrary text into the field (defaults to false)
39519      */
39520     forceSelection:false,
39521     /**
39522      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39523      * if typeAhead = true (defaults to 250)
39524      */
39525     typeAheadDelay : 250,
39526     /**
39527      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39528      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39529      */
39530     valueNotFoundText : undefined,
39531     /**
39532      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39533      */
39534     blockFocus : false,
39535     
39536     /**
39537      * @cfg {Boolean} disableClear Disable showing of clear button.
39538      */
39539     disableClear : false,
39540     /**
39541      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39542      */
39543     alwaysQuery : false,
39544     
39545     //private
39546     addicon : false,
39547     editicon: false,
39548     
39549     // element that contains real text value.. (when hidden is used..)
39550      
39551     // private
39552     onRender : function(ct, position){
39553         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39554         if(this.hiddenName){
39555             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39556                     'before', true);
39557             this.hiddenField.value =
39558                 this.hiddenValue !== undefined ? this.hiddenValue :
39559                 this.value !== undefined ? this.value : '';
39560
39561             // prevent input submission
39562             this.el.dom.removeAttribute('name');
39563              
39564              
39565         }
39566         if(Roo.isGecko){
39567             this.el.dom.setAttribute('autocomplete', 'off');
39568         }
39569
39570         var cls = 'x-combo-list';
39571
39572         this.list = new Roo.Layer({
39573             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39574         });
39575
39576         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39577         this.list.setWidth(lw);
39578         this.list.swallowEvent('mousewheel');
39579         this.assetHeight = 0;
39580
39581         if(this.title){
39582             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39583             this.assetHeight += this.header.getHeight();
39584         }
39585
39586         this.innerList = this.list.createChild({cls:cls+'-inner'});
39587         this.innerList.on('mouseover', this.onViewOver, this);
39588         this.innerList.on('mousemove', this.onViewMove, this);
39589         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39590         
39591         if(this.allowBlank && !this.pageSize && !this.disableClear){
39592             this.footer = this.list.createChild({cls:cls+'-ft'});
39593             this.pageTb = new Roo.Toolbar(this.footer);
39594            
39595         }
39596         if(this.pageSize){
39597             this.footer = this.list.createChild({cls:cls+'-ft'});
39598             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39599                     {pageSize: this.pageSize});
39600             
39601         }
39602         
39603         if (this.pageTb && this.allowBlank && !this.disableClear) {
39604             var _this = this;
39605             this.pageTb.add(new Roo.Toolbar.Fill(), {
39606                 cls: 'x-btn-icon x-btn-clear',
39607                 text: '&#160;',
39608                 handler: function()
39609                 {
39610                     _this.collapse();
39611                     _this.clearValue();
39612                     _this.onSelect(false, -1);
39613                 }
39614             });
39615         }
39616         if (this.footer) {
39617             this.assetHeight += this.footer.getHeight();
39618         }
39619         
39620
39621         if(!this.tpl){
39622             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39623         }
39624
39625         this.view = new Roo.View(this.innerList, this.tpl, {
39626             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39627         });
39628
39629         this.view.on('click', this.onViewClick, this);
39630
39631         this.store.on('beforeload', this.onBeforeLoad, this);
39632         this.store.on('load', this.onLoad, this);
39633         this.store.on('loadexception', this.onLoadException, this);
39634
39635         if(this.resizable){
39636             this.resizer = new Roo.Resizable(this.list,  {
39637                pinned:true, handles:'se'
39638             });
39639             this.resizer.on('resize', function(r, w, h){
39640                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39641                 this.listWidth = w;
39642                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39643                 this.restrictHeight();
39644             }, this);
39645             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39646         }
39647         if(!this.editable){
39648             this.editable = true;
39649             this.setEditable(false);
39650         }  
39651         
39652         
39653         if (typeof(this.events.add.listeners) != 'undefined') {
39654             
39655             this.addicon = this.wrap.createChild(
39656                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39657        
39658             this.addicon.on('click', function(e) {
39659                 this.fireEvent('add', this);
39660             }, this);
39661         }
39662         if (typeof(this.events.edit.listeners) != 'undefined') {
39663             
39664             this.editicon = this.wrap.createChild(
39665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39666             if (this.addicon) {
39667                 this.editicon.setStyle('margin-left', '40px');
39668             }
39669             this.editicon.on('click', function(e) {
39670                 
39671                 // we fire even  if inothing is selected..
39672                 this.fireEvent('edit', this, this.lastData );
39673                 
39674             }, this);
39675         }
39676         
39677         
39678         
39679     },
39680
39681     // private
39682     initEvents : function(){
39683         Roo.form.ComboBox.superclass.initEvents.call(this);
39684
39685         this.keyNav = new Roo.KeyNav(this.el, {
39686             "up" : function(e){
39687                 this.inKeyMode = true;
39688                 this.selectPrev();
39689             },
39690
39691             "down" : function(e){
39692                 if(!this.isExpanded()){
39693                     this.onTriggerClick();
39694                 }else{
39695                     this.inKeyMode = true;
39696                     this.selectNext();
39697                 }
39698             },
39699
39700             "enter" : function(e){
39701                 this.onViewClick();
39702                 //return true;
39703             },
39704
39705             "esc" : function(e){
39706                 this.collapse();
39707             },
39708
39709             "tab" : function(e){
39710                 this.onViewClick(false);
39711                 this.fireEvent("specialkey", this, e);
39712                 return true;
39713             },
39714
39715             scope : this,
39716
39717             doRelay : function(foo, bar, hname){
39718                 if(hname == 'down' || this.scope.isExpanded()){
39719                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39720                 }
39721                 return true;
39722             },
39723
39724             forceKeyDown: true
39725         });
39726         this.queryDelay = Math.max(this.queryDelay || 10,
39727                 this.mode == 'local' ? 10 : 250);
39728         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39729         if(this.typeAhead){
39730             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39731         }
39732         if(this.editable !== false){
39733             this.el.on("keyup", this.onKeyUp, this);
39734         }
39735         if(this.forceSelection){
39736             this.on('blur', this.doForce, this);
39737         }
39738     },
39739
39740     onDestroy : function(){
39741         if(this.view){
39742             this.view.setStore(null);
39743             this.view.el.removeAllListeners();
39744             this.view.el.remove();
39745             this.view.purgeListeners();
39746         }
39747         if(this.list){
39748             this.list.destroy();
39749         }
39750         if(this.store){
39751             this.store.un('beforeload', this.onBeforeLoad, this);
39752             this.store.un('load', this.onLoad, this);
39753             this.store.un('loadexception', this.onLoadException, this);
39754         }
39755         Roo.form.ComboBox.superclass.onDestroy.call(this);
39756     },
39757
39758     // private
39759     fireKey : function(e){
39760         if(e.isNavKeyPress() && !this.list.isVisible()){
39761             this.fireEvent("specialkey", this, e);
39762         }
39763     },
39764
39765     // private
39766     onResize: function(w, h){
39767         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39768         
39769         if(typeof w != 'number'){
39770             // we do not handle it!?!?
39771             return;
39772         }
39773         var tw = this.trigger.getWidth();
39774         tw += this.addicon ? this.addicon.getWidth() : 0;
39775         tw += this.editicon ? this.editicon.getWidth() : 0;
39776         var x = w - tw;
39777         this.el.setWidth( this.adjustWidth('input', x));
39778             
39779         this.trigger.setStyle('left', x+'px');
39780         
39781         if(this.list && this.listWidth === undefined){
39782             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39783             this.list.setWidth(lw);
39784             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39785         }
39786         
39787     
39788         
39789     },
39790
39791     /**
39792      * Allow or prevent the user from directly editing the field text.  If false is passed,
39793      * the user will only be able to select from the items defined in the dropdown list.  This method
39794      * is the runtime equivalent of setting the 'editable' config option at config time.
39795      * @param {Boolean} value True to allow the user to directly edit the field text
39796      */
39797     setEditable : function(value){
39798         if(value == this.editable){
39799             return;
39800         }
39801         this.editable = value;
39802         if(!value){
39803             this.el.dom.setAttribute('readOnly', true);
39804             this.el.on('mousedown', this.onTriggerClick,  this);
39805             this.el.addClass('x-combo-noedit');
39806         }else{
39807             this.el.dom.setAttribute('readOnly', false);
39808             this.el.un('mousedown', this.onTriggerClick,  this);
39809             this.el.removeClass('x-combo-noedit');
39810         }
39811     },
39812
39813     // private
39814     onBeforeLoad : function(){
39815         if(!this.hasFocus){
39816             return;
39817         }
39818         this.innerList.update(this.loadingText ?
39819                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39820         this.restrictHeight();
39821         this.selectedIndex = -1;
39822     },
39823
39824     // private
39825     onLoad : function(){
39826         if(!this.hasFocus){
39827             return;
39828         }
39829         if(this.store.getCount() > 0){
39830             this.expand();
39831             this.restrictHeight();
39832             if(this.lastQuery == this.allQuery){
39833                 if(this.editable){
39834                     this.el.dom.select();
39835                 }
39836                 if(!this.selectByValue(this.value, true)){
39837                     this.select(0, true);
39838                 }
39839             }else{
39840                 this.selectNext();
39841                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39842                     this.taTask.delay(this.typeAheadDelay);
39843                 }
39844             }
39845         }else{
39846             this.onEmptyResults();
39847         }
39848         //this.el.focus();
39849     },
39850     // private
39851     onLoadException : function()
39852     {
39853         this.collapse();
39854         Roo.log(this.store.reader.jsonData);
39855         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39856             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39857         }
39858         
39859         
39860     },
39861     // private
39862     onTypeAhead : function(){
39863         if(this.store.getCount() > 0){
39864             var r = this.store.getAt(0);
39865             var newValue = r.data[this.displayField];
39866             var len = newValue.length;
39867             var selStart = this.getRawValue().length;
39868             if(selStart != len){
39869                 this.setRawValue(newValue);
39870                 this.selectText(selStart, newValue.length);
39871             }
39872         }
39873     },
39874
39875     // private
39876     onSelect : function(record, index){
39877         if(this.fireEvent('beforeselect', this, record, index) !== false){
39878             this.setFromData(index > -1 ? record.data : false);
39879             this.collapse();
39880             this.fireEvent('select', this, record, index);
39881         }
39882     },
39883
39884     /**
39885      * Returns the currently selected field value or empty string if no value is set.
39886      * @return {String} value The selected value
39887      */
39888     getValue : function(){
39889         if(this.valueField){
39890             return typeof this.value != 'undefined' ? this.value : '';
39891         }
39892         return Roo.form.ComboBox.superclass.getValue.call(this);
39893     },
39894
39895     /**
39896      * Clears any text/value currently set in the field
39897      */
39898     clearValue : function(){
39899         if(this.hiddenField){
39900             this.hiddenField.value = '';
39901         }
39902         this.value = '';
39903         this.setRawValue('');
39904         this.lastSelectionText = '';
39905         
39906     },
39907
39908     /**
39909      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39910      * will be displayed in the field.  If the value does not match the data value of an existing item,
39911      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39912      * Otherwise the field will be blank (although the value will still be set).
39913      * @param {String} value The value to match
39914      */
39915     setValue : function(v){
39916         var text = v;
39917         if(this.valueField){
39918             var r = this.findRecord(this.valueField, v);
39919             if(r){
39920                 text = r.data[this.displayField];
39921             }else if(this.valueNotFoundText !== undefined){
39922                 text = this.valueNotFoundText;
39923             }
39924         }
39925         this.lastSelectionText = text;
39926         if(this.hiddenField){
39927             this.hiddenField.value = v;
39928         }
39929         Roo.form.ComboBox.superclass.setValue.call(this, text);
39930         this.value = v;
39931     },
39932     /**
39933      * @property {Object} the last set data for the element
39934      */
39935     
39936     lastData : false,
39937     /**
39938      * Sets the value of the field based on a object which is related to the record format for the store.
39939      * @param {Object} value the value to set as. or false on reset?
39940      */
39941     setFromData : function(o){
39942         var dv = ''; // display value
39943         var vv = ''; // value value..
39944         this.lastData = o;
39945         if (this.displayField) {
39946             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39947         } else {
39948             // this is an error condition!!!
39949             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39950         }
39951         
39952         if(this.valueField){
39953             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39954         }
39955         if(this.hiddenField){
39956             this.hiddenField.value = vv;
39957             
39958             this.lastSelectionText = dv;
39959             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39960             this.value = vv;
39961             return;
39962         }
39963         // no hidden field.. - we store the value in 'value', but still display
39964         // display field!!!!
39965         this.lastSelectionText = dv;
39966         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39967         this.value = vv;
39968         
39969         
39970     },
39971     // private
39972     reset : function(){
39973         // overridden so that last data is reset..
39974         this.setValue(this.resetValue);
39975         this.clearInvalid();
39976         this.lastData = false;
39977         if (this.view) {
39978             this.view.clearSelections();
39979         }
39980     },
39981     // private
39982     findRecord : function(prop, value){
39983         var record;
39984         if(this.store.getCount() > 0){
39985             this.store.each(function(r){
39986                 if(r.data[prop] == value){
39987                     record = r;
39988                     return false;
39989                 }
39990                 return true;
39991             });
39992         }
39993         return record;
39994     },
39995     
39996     getName: function()
39997     {
39998         // returns hidden if it's set..
39999         if (!this.rendered) {return ''};
40000         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40001         
40002     },
40003     // private
40004     onViewMove : function(e, t){
40005         this.inKeyMode = false;
40006     },
40007
40008     // private
40009     onViewOver : function(e, t){
40010         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40011             return;
40012         }
40013         var item = this.view.findItemFromChild(t);
40014         if(item){
40015             var index = this.view.indexOf(item);
40016             this.select(index, false);
40017         }
40018     },
40019
40020     // private
40021     onViewClick : function(doFocus)
40022     {
40023         var index = this.view.getSelectedIndexes()[0];
40024         var r = this.store.getAt(index);
40025         if(r){
40026             this.onSelect(r, index);
40027         }
40028         if(doFocus !== false && !this.blockFocus){
40029             this.el.focus();
40030         }
40031     },
40032
40033     // private
40034     restrictHeight : function(){
40035         this.innerList.dom.style.height = '';
40036         var inner = this.innerList.dom;
40037         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40038         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40039         this.list.beginUpdate();
40040         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40041         this.list.alignTo(this.el, this.listAlign);
40042         this.list.endUpdate();
40043     },
40044
40045     // private
40046     onEmptyResults : function(){
40047         this.collapse();
40048     },
40049
40050     /**
40051      * Returns true if the dropdown list is expanded, else false.
40052      */
40053     isExpanded : function(){
40054         return this.list.isVisible();
40055     },
40056
40057     /**
40058      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40059      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40060      * @param {String} value The data value of the item to select
40061      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40062      * selected item if it is not currently in view (defaults to true)
40063      * @return {Boolean} True if the value matched an item in the list, else false
40064      */
40065     selectByValue : function(v, scrollIntoView){
40066         if(v !== undefined && v !== null){
40067             var r = this.findRecord(this.valueField || this.displayField, v);
40068             if(r){
40069                 this.select(this.store.indexOf(r), scrollIntoView);
40070                 return true;
40071             }
40072         }
40073         return false;
40074     },
40075
40076     /**
40077      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40079      * @param {Number} index The zero-based index of the list item to select
40080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40081      * selected item if it is not currently in view (defaults to true)
40082      */
40083     select : function(index, scrollIntoView){
40084         this.selectedIndex = index;
40085         this.view.select(index);
40086         if(scrollIntoView !== false){
40087             var el = this.view.getNode(index);
40088             if(el){
40089                 this.innerList.scrollChildIntoView(el, false);
40090             }
40091         }
40092     },
40093
40094     // private
40095     selectNext : function(){
40096         var ct = this.store.getCount();
40097         if(ct > 0){
40098             if(this.selectedIndex == -1){
40099                 this.select(0);
40100             }else if(this.selectedIndex < ct-1){
40101                 this.select(this.selectedIndex+1);
40102             }
40103         }
40104     },
40105
40106     // private
40107     selectPrev : function(){
40108         var ct = this.store.getCount();
40109         if(ct > 0){
40110             if(this.selectedIndex == -1){
40111                 this.select(0);
40112             }else if(this.selectedIndex != 0){
40113                 this.select(this.selectedIndex-1);
40114             }
40115         }
40116     },
40117
40118     // private
40119     onKeyUp : function(e){
40120         if(this.editable !== false && !e.isSpecialKey()){
40121             this.lastKey = e.getKey();
40122             this.dqTask.delay(this.queryDelay);
40123         }
40124     },
40125
40126     // private
40127     validateBlur : function(){
40128         return !this.list || !this.list.isVisible();   
40129     },
40130
40131     // private
40132     initQuery : function(){
40133         this.doQuery(this.getRawValue());
40134     },
40135
40136     // private
40137     doForce : function(){
40138         if(this.el.dom.value.length > 0){
40139             this.el.dom.value =
40140                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40141              
40142         }
40143     },
40144
40145     /**
40146      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40147      * query allowing the query action to be canceled if needed.
40148      * @param {String} query The SQL query to execute
40149      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40150      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40151      * saved in the current store (defaults to false)
40152      */
40153     doQuery : function(q, forceAll){
40154         if(q === undefined || q === null){
40155             q = '';
40156         }
40157         var qe = {
40158             query: q,
40159             forceAll: forceAll,
40160             combo: this,
40161             cancel:false
40162         };
40163         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40164             return false;
40165         }
40166         q = qe.query;
40167         forceAll = qe.forceAll;
40168         if(forceAll === true || (q.length >= this.minChars)){
40169             if(this.lastQuery != q || this.alwaysQuery){
40170                 this.lastQuery = q;
40171                 if(this.mode == 'local'){
40172                     this.selectedIndex = -1;
40173                     if(forceAll){
40174                         this.store.clearFilter();
40175                     }else{
40176                         this.store.filter(this.displayField, q);
40177                     }
40178                     this.onLoad();
40179                 }else{
40180                     this.store.baseParams[this.queryParam] = q;
40181                     this.store.load({
40182                         params: this.getParams(q)
40183                     });
40184                     this.expand();
40185                 }
40186             }else{
40187                 this.selectedIndex = -1;
40188                 this.onLoad();   
40189             }
40190         }
40191     },
40192
40193     // private
40194     getParams : function(q){
40195         var p = {};
40196         //p[this.queryParam] = q;
40197         if(this.pageSize){
40198             p.start = 0;
40199             p.limit = this.pageSize;
40200         }
40201         return p;
40202     },
40203
40204     /**
40205      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40206      */
40207     collapse : function(){
40208         if(!this.isExpanded()){
40209             return;
40210         }
40211         this.list.hide();
40212         Roo.get(document).un('mousedown', this.collapseIf, this);
40213         Roo.get(document).un('mousewheel', this.collapseIf, this);
40214         if (!this.editable) {
40215             Roo.get(document).un('keydown', this.listKeyPress, this);
40216         }
40217         this.fireEvent('collapse', this);
40218     },
40219
40220     // private
40221     collapseIf : function(e){
40222         if(!e.within(this.wrap) && !e.within(this.list)){
40223             this.collapse();
40224         }
40225     },
40226
40227     /**
40228      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40229      */
40230     expand : function(){
40231         if(this.isExpanded() || !this.hasFocus){
40232             return;
40233         }
40234         this.list.alignTo(this.el, this.listAlign);
40235         this.list.show();
40236         Roo.get(document).on('mousedown', this.collapseIf, this);
40237         Roo.get(document).on('mousewheel', this.collapseIf, this);
40238         if (!this.editable) {
40239             Roo.get(document).on('keydown', this.listKeyPress, this);
40240         }
40241         
40242         this.fireEvent('expand', this);
40243     },
40244
40245     // private
40246     // Implements the default empty TriggerField.onTriggerClick function
40247     onTriggerClick : function(){
40248         if(this.disabled){
40249             return;
40250         }
40251         if(this.isExpanded()){
40252             this.collapse();
40253             if (!this.blockFocus) {
40254                 this.el.focus();
40255             }
40256             
40257         }else {
40258             this.hasFocus = true;
40259             if(this.triggerAction == 'all') {
40260                 this.doQuery(this.allQuery, true);
40261             } else {
40262                 this.doQuery(this.getRawValue());
40263             }
40264             if (!this.blockFocus) {
40265                 this.el.focus();
40266             }
40267         }
40268     },
40269     listKeyPress : function(e)
40270     {
40271         //Roo.log('listkeypress');
40272         // scroll to first matching element based on key pres..
40273         if (e.isSpecialKey()) {
40274             return false;
40275         }
40276         var k = String.fromCharCode(e.getKey()).toUpperCase();
40277         //Roo.log(k);
40278         var match  = false;
40279         var csel = this.view.getSelectedNodes();
40280         var cselitem = false;
40281         if (csel.length) {
40282             var ix = this.view.indexOf(csel[0]);
40283             cselitem  = this.store.getAt(ix);
40284             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40285                 cselitem = false;
40286             }
40287             
40288         }
40289         
40290         this.store.each(function(v) { 
40291             if (cselitem) {
40292                 // start at existing selection.
40293                 if (cselitem.id == v.id) {
40294                     cselitem = false;
40295                 }
40296                 return;
40297             }
40298                 
40299             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40300                 match = this.store.indexOf(v);
40301                 return false;
40302             }
40303         }, this);
40304         
40305         if (match === false) {
40306             return true; // no more action?
40307         }
40308         // scroll to?
40309         this.view.select(match);
40310         var sn = Roo.get(this.view.getSelectedNodes()[0])
40311         sn.scrollIntoView(sn.dom.parentNode, false);
40312     }
40313
40314     /** 
40315     * @cfg {Boolean} grow 
40316     * @hide 
40317     */
40318     /** 
40319     * @cfg {Number} growMin 
40320     * @hide 
40321     */
40322     /** 
40323     * @cfg {Number} growMax 
40324     * @hide 
40325     */
40326     /**
40327      * @hide
40328      * @method autoSize
40329      */
40330 });/*
40331  * Copyright(c) 2010-2012, Roo J Solutions Limited
40332  *
40333  * Licence LGPL
40334  *
40335  */
40336
40337 /**
40338  * @class Roo.form.ComboBoxArray
40339  * @extends Roo.form.TextField
40340  * A facebook style adder... for lists of email / people / countries  etc...
40341  * pick multiple items from a combo box, and shows each one.
40342  *
40343  *  Fred [x]  Brian [x]  [Pick another |v]
40344  *
40345  *
40346  *  For this to work: it needs various extra information
40347  *    - normal combo problay has
40348  *      name, hiddenName
40349  *    + displayField, valueField
40350  *
40351  *    For our purpose...
40352  *
40353  *
40354  *   If we change from 'extends' to wrapping...
40355  *   
40356  *  
40357  *
40358  
40359  
40360  * @constructor
40361  * Create a new ComboBoxArray.
40362  * @param {Object} config Configuration options
40363  */
40364  
40365
40366 Roo.form.ComboBoxArray = function(config)
40367 {
40368     this.addEvents({
40369         /**
40370          * @event beforeremove
40371          * Fires before remove the value from the list
40372              * @param {Roo.form.ComboBoxArray} _self This combo box array
40373              * @param {Roo.form.ComboBoxArray.Item} item removed item
40374              */
40375         'beforeremove' : true,
40376         /**
40377          * @event remove
40378          * Fires when remove the value from the list
40379              * @param {Roo.form.ComboBoxArray} _self This combo box array
40380              * @param {Roo.form.ComboBoxArray.Item} item removed item
40381              */
40382         'remove' : true
40383         
40384         
40385     });
40386     
40387     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40388     
40389     this.items = new Roo.util.MixedCollection(false);
40390     
40391     // construct the child combo...
40392     
40393     
40394     
40395     
40396    
40397     
40398 }
40399
40400  
40401 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40402
40403     /**
40404      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40405      */
40406     
40407     lastData : false,
40408     
40409     // behavies liek a hiddne field
40410     inputType:      'hidden',
40411     /**
40412      * @cfg {Number} width The width of the box that displays the selected element
40413      */ 
40414     width:          300,
40415
40416     
40417     
40418     /**
40419      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40420      */
40421     name : false,
40422     /**
40423      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40424      */
40425     hiddenName : false,
40426     
40427     
40428     // private the array of items that are displayed..
40429     items  : false,
40430     // private - the hidden field el.
40431     hiddenEl : false,
40432     // private - the filed el..
40433     el : false,
40434     
40435     //validateValue : function() { return true; }, // all values are ok!
40436     //onAddClick: function() { },
40437     
40438     onRender : function(ct, position) 
40439     {
40440         
40441         // create the standard hidden element
40442         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40443         
40444         
40445         // give fake names to child combo;
40446         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40447         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40448         
40449         this.combo = Roo.factory(this.combo, Roo.form);
40450         this.combo.onRender(ct, position);
40451         if (typeof(this.combo.width) != 'undefined') {
40452             this.combo.onResize(this.combo.width,0);
40453         }
40454         
40455         this.combo.initEvents();
40456         
40457         // assigned so form know we need to do this..
40458         this.store          = this.combo.store;
40459         this.valueField     = this.combo.valueField;
40460         this.displayField   = this.combo.displayField ;
40461         
40462         
40463         this.combo.wrap.addClass('x-cbarray-grp');
40464         
40465         var cbwrap = this.combo.wrap.createChild(
40466             {tag: 'div', cls: 'x-cbarray-cb'},
40467             this.combo.el.dom
40468         );
40469         
40470              
40471         this.hiddenEl = this.combo.wrap.createChild({
40472             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40473         });
40474         this.el = this.combo.wrap.createChild({
40475             tag: 'input',  type:'hidden' , name: this.name, value : ''
40476         });
40477          //   this.el.dom.removeAttribute("name");
40478         
40479         
40480         this.outerWrap = this.combo.wrap;
40481         this.wrap = cbwrap;
40482         
40483         this.outerWrap.setWidth(this.width);
40484         this.outerWrap.dom.removeChild(this.el.dom);
40485         
40486         this.wrap.dom.appendChild(this.el.dom);
40487         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40488         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40489         
40490         this.combo.trigger.setStyle('position','relative');
40491         this.combo.trigger.setStyle('left', '0px');
40492         this.combo.trigger.setStyle('top', '2px');
40493         
40494         this.combo.el.setStyle('vertical-align', 'text-bottom');
40495         
40496         //this.trigger.setStyle('vertical-align', 'top');
40497         
40498         // this should use the code from combo really... on('add' ....)
40499         if (this.adder) {
40500             
40501         
40502             this.adder = this.outerWrap.createChild(
40503                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40504             var _t = this;
40505             this.adder.on('click', function(e) {
40506                 _t.fireEvent('adderclick', this, e);
40507             }, _t);
40508         }
40509         //var _t = this;
40510         //this.adder.on('click', this.onAddClick, _t);
40511         
40512         
40513         this.combo.on('select', function(cb, rec, ix) {
40514             this.addItem(rec.data);
40515             
40516             cb.setValue('');
40517             cb.el.dom.value = '';
40518             //cb.lastData = rec.data;
40519             // add to list
40520             
40521         }, this);
40522         
40523         
40524     },
40525     
40526     
40527     getName: function()
40528     {
40529         // returns hidden if it's set..
40530         if (!this.rendered) {return ''};
40531         return  this.hiddenName ? this.hiddenName : this.name;
40532         
40533     },
40534     
40535     
40536     onResize: function(w, h){
40537         
40538         return;
40539         // not sure if this is needed..
40540         //this.combo.onResize(w,h);
40541         
40542         if(typeof w != 'number'){
40543             // we do not handle it!?!?
40544             return;
40545         }
40546         var tw = this.combo.trigger.getWidth();
40547         tw += this.addicon ? this.addicon.getWidth() : 0;
40548         tw += this.editicon ? this.editicon.getWidth() : 0;
40549         var x = w - tw;
40550         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40551             
40552         this.combo.trigger.setStyle('left', '0px');
40553         
40554         if(this.list && this.listWidth === undefined){
40555             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40556             this.list.setWidth(lw);
40557             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40558         }
40559         
40560     
40561         
40562     },
40563     
40564     addItem: function(rec)
40565     {
40566         var valueField = this.combo.valueField;
40567         var displayField = this.combo.displayField;
40568         if (this.items.indexOfKey(rec[valueField]) > -1) {
40569             //console.log("GOT " + rec.data.id);
40570             return;
40571         }
40572         
40573         var x = new Roo.form.ComboBoxArray.Item({
40574             //id : rec[this.idField],
40575             data : rec,
40576             displayField : displayField ,
40577             tipField : displayField ,
40578             cb : this
40579         });
40580         // use the 
40581         this.items.add(rec[valueField],x);
40582         // add it before the element..
40583         this.updateHiddenEl();
40584         x.render(this.outerWrap, this.wrap.dom);
40585         // add the image handler..
40586     },
40587     
40588     updateHiddenEl : function()
40589     {
40590         this.validate();
40591         if (!this.hiddenEl) {
40592             return;
40593         }
40594         var ar = [];
40595         var idField = this.combo.valueField;
40596         
40597         this.items.each(function(f) {
40598             ar.push(f.data[idField]);
40599            
40600         });
40601         this.hiddenEl.dom.value = ar.join(',');
40602         this.validate();
40603     },
40604     
40605     reset : function()
40606     {
40607         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40608         this.items.each(function(f) {
40609            f.remove(); 
40610         });
40611         this.el.dom.value = '';
40612         if (this.hiddenEl) {
40613             this.hiddenEl.dom.value = '';
40614         }
40615         
40616     },
40617     getValue: function()
40618     {
40619         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40620     },
40621     setValue: function(v) // not a valid action - must use addItems..
40622     {
40623          
40624         this.reset();
40625         
40626         
40627         
40628         if (this.store.isLocal && (typeof(v) == 'string')) {
40629             // then we can use the store to find the values..
40630             // comma seperated at present.. this needs to allow JSON based encoding..
40631             this.hiddenEl.value  = v;
40632             var v_ar = [];
40633             Roo.each(v.split(','), function(k) {
40634                 Roo.log("CHECK " + this.valueField + ',' + k);
40635                 var li = this.store.query(this.valueField, k);
40636                 if (!li.length) {
40637                     return;
40638                 }
40639                 var add = {};
40640                 add[this.valueField] = k;
40641                 add[this.displayField] = li.item(0).data[this.displayField];
40642                 
40643                 this.addItem(add);
40644             }, this) 
40645              
40646         }
40647         if (typeof(v) == 'object' ) {
40648             // then let's assume it's an array of objects..
40649             Roo.each(v, function(l) {
40650                 this.addItem(l);
40651             }, this);
40652              
40653         }
40654         
40655         
40656     },
40657     setFromData: function(v)
40658     {
40659         // this recieves an object, if setValues is called.
40660         this.reset();
40661         this.el.dom.value = v[this.displayField];
40662         this.hiddenEl.dom.value = v[this.valueField];
40663         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40664             return;
40665         }
40666         var kv = v[this.valueField];
40667         var dv = v[this.displayField];
40668         kv = typeof(kv) != 'string' ? '' : kv;
40669         dv = typeof(dv) != 'string' ? '' : dv;
40670         
40671         
40672         var keys = kv.split(',');
40673         var display = dv.split(',');
40674         for (var i = 0 ; i < keys.length; i++) {
40675             
40676             add = {};
40677             add[this.valueField] = keys[i];
40678             add[this.displayField] = display[i];
40679             this.addItem(add);
40680         }
40681       
40682         
40683     },
40684     
40685     /**
40686      * Validates the combox array value
40687      * @return {Boolean} True if the value is valid, else false
40688      */
40689     validate : function(){
40690         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40691             this.clearInvalid();
40692             return true;
40693         }
40694         return false;
40695     },
40696     
40697     validateValue : function(value){
40698         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40699         
40700     },
40701     
40702     /*@
40703      * overide
40704      * 
40705      */
40706     isDirty : function() {
40707         if(this.disabled) {
40708             return false;
40709         }
40710         
40711         try {
40712             var d = Roo.decode(String(this.originalValue));
40713         } catch (e) {
40714             return String(this.getValue()) !== String(this.originalValue);
40715         }
40716         
40717         var originalValue = [];
40718         
40719         for (var i = 0; i < d.length; i++){
40720             originalValue.push(d[i][this.valueField]);
40721         }
40722         
40723         return String(this.getValue()) !== String(originalValue.join(','));
40724         
40725     }
40726     
40727 });
40728
40729
40730
40731 /**
40732  * @class Roo.form.ComboBoxArray.Item
40733  * @extends Roo.BoxComponent
40734  * A selected item in the list
40735  *  Fred [x]  Brian [x]  [Pick another |v]
40736  * 
40737  * @constructor
40738  * Create a new item.
40739  * @param {Object} config Configuration options
40740  */
40741  
40742 Roo.form.ComboBoxArray.Item = function(config) {
40743     config.id = Roo.id();
40744     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40745 }
40746
40747 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40748     data : {},
40749     cb: false,
40750     displayField : false,
40751     tipField : false,
40752     
40753     
40754     defaultAutoCreate : {
40755         tag: 'div',
40756         cls: 'x-cbarray-item',
40757         cn : [ 
40758             { tag: 'div' },
40759             {
40760                 tag: 'img',
40761                 width:16,
40762                 height : 16,
40763                 src : Roo.BLANK_IMAGE_URL ,
40764                 align: 'center'
40765             }
40766         ]
40767         
40768     },
40769     
40770  
40771     onRender : function(ct, position)
40772     {
40773         Roo.form.Field.superclass.onRender.call(this, ct, position);
40774         
40775         if(!this.el){
40776             var cfg = this.getAutoCreate();
40777             this.el = ct.createChild(cfg, position);
40778         }
40779         
40780         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40781         
40782         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40783             this.cb.renderer(this.data) :
40784             String.format('{0}',this.data[this.displayField]);
40785         
40786             
40787         this.el.child('div').dom.setAttribute('qtip',
40788                         String.format('{0}',this.data[this.tipField])
40789         );
40790         
40791         this.el.child('img').on('click', this.remove, this);
40792         
40793     },
40794    
40795     remove : function()
40796     {
40797         if(this.cb.disabled){
40798             return;
40799         }
40800         
40801         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40802             this.cb.items.remove(this);
40803             this.el.child('img').un('click', this.remove, this);
40804             this.el.remove();
40805             this.cb.updateHiddenEl();
40806
40807             this.cb.fireEvent('remove', this.cb, this);
40808         }
40809         
40810     }
40811 });/*
40812  * Based on:
40813  * Ext JS Library 1.1.1
40814  * Copyright(c) 2006-2007, Ext JS, LLC.
40815  *
40816  * Originally Released Under LGPL - original licence link has changed is not relivant.
40817  *
40818  * Fork - LGPL
40819  * <script type="text/javascript">
40820  */
40821 /**
40822  * @class Roo.form.Checkbox
40823  * @extends Roo.form.Field
40824  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40825  * @constructor
40826  * Creates a new Checkbox
40827  * @param {Object} config Configuration options
40828  */
40829 Roo.form.Checkbox = function(config){
40830     Roo.form.Checkbox.superclass.constructor.call(this, config);
40831     this.addEvents({
40832         /**
40833          * @event check
40834          * Fires when the checkbox is checked or unchecked.
40835              * @param {Roo.form.Checkbox} this This checkbox
40836              * @param {Boolean} checked The new checked value
40837              */
40838         check : true
40839     });
40840 };
40841
40842 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40843     /**
40844      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40845      */
40846     focusClass : undefined,
40847     /**
40848      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40849      */
40850     fieldClass: "x-form-field",
40851     /**
40852      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40853      */
40854     checked: false,
40855     /**
40856      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40857      * {tag: "input", type: "checkbox", autocomplete: "off"})
40858      */
40859     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40860     /**
40861      * @cfg {String} boxLabel The text that appears beside the checkbox
40862      */
40863     boxLabel : "",
40864     /**
40865      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40866      */  
40867     inputValue : '1',
40868     /**
40869      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40870      */
40871      valueOff: '0', // value when not checked..
40872
40873     actionMode : 'viewEl', 
40874     //
40875     // private
40876     itemCls : 'x-menu-check-item x-form-item',
40877     groupClass : 'x-menu-group-item',
40878     inputType : 'hidden',
40879     
40880     
40881     inSetChecked: false, // check that we are not calling self...
40882     
40883     inputElement: false, // real input element?
40884     basedOn: false, // ????
40885     
40886     isFormField: true, // not sure where this is needed!!!!
40887
40888     onResize : function(){
40889         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40890         if(!this.boxLabel){
40891             this.el.alignTo(this.wrap, 'c-c');
40892         }
40893     },
40894
40895     initEvents : function(){
40896         Roo.form.Checkbox.superclass.initEvents.call(this);
40897         this.el.on("click", this.onClick,  this);
40898         this.el.on("change", this.onClick,  this);
40899     },
40900
40901
40902     getResizeEl : function(){
40903         return this.wrap;
40904     },
40905
40906     getPositionEl : function(){
40907         return this.wrap;
40908     },
40909
40910     // private
40911     onRender : function(ct, position){
40912         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40913         /*
40914         if(this.inputValue !== undefined){
40915             this.el.dom.value = this.inputValue;
40916         }
40917         */
40918         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40919         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40920         var viewEl = this.wrap.createChild({ 
40921             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40922         this.viewEl = viewEl;   
40923         this.wrap.on('click', this.onClick,  this); 
40924         
40925         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40926         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40927         
40928         
40929         
40930         if(this.boxLabel){
40931             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40932         //    viewEl.on('click', this.onClick,  this); 
40933         }
40934         //if(this.checked){
40935             this.setChecked(this.checked);
40936         //}else{
40937             //this.checked = this.el.dom;
40938         //}
40939
40940     },
40941
40942     // private
40943     initValue : Roo.emptyFn,
40944
40945     /**
40946      * Returns the checked state of the checkbox.
40947      * @return {Boolean} True if checked, else false
40948      */
40949     getValue : function(){
40950         if(this.el){
40951             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40952         }
40953         return this.valueOff;
40954         
40955     },
40956
40957         // private
40958     onClick : function(){ 
40959         if (this.disabled) {
40960             return;
40961         }
40962         this.setChecked(!this.checked);
40963
40964         //if(this.el.dom.checked != this.checked){
40965         //    this.setValue(this.el.dom.checked);
40966        // }
40967     },
40968
40969     /**
40970      * Sets the checked state of the checkbox.
40971      * On is always based on a string comparison between inputValue and the param.
40972      * @param {Boolean/String} value - the value to set 
40973      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40974      */
40975     setValue : function(v,suppressEvent){
40976         
40977         
40978         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40979         //if(this.el && this.el.dom){
40980         //    this.el.dom.checked = this.checked;
40981         //    this.el.dom.defaultChecked = this.checked;
40982         //}
40983         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40984         //this.fireEvent("check", this, this.checked);
40985     },
40986     // private..
40987     setChecked : function(state,suppressEvent)
40988     {
40989         if (this.inSetChecked) {
40990             this.checked = state;
40991             return;
40992         }
40993         
40994     
40995         if(this.wrap){
40996             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40997         }
40998         this.checked = state;
40999         if(suppressEvent !== true){
41000             this.fireEvent('check', this, state);
41001         }
41002         this.inSetChecked = true;
41003         this.el.dom.value = state ? this.inputValue : this.valueOff;
41004         this.inSetChecked = false;
41005         
41006     },
41007     // handle setting of hidden value by some other method!!?!?
41008     setFromHidden: function()
41009     {
41010         if(!this.el){
41011             return;
41012         }
41013         //console.log("SET FROM HIDDEN");
41014         //alert('setFrom hidden');
41015         this.setValue(this.el.dom.value);
41016     },
41017     
41018     onDestroy : function()
41019     {
41020         if(this.viewEl){
41021             Roo.get(this.viewEl).remove();
41022         }
41023          
41024         Roo.form.Checkbox.superclass.onDestroy.call(this);
41025     }
41026
41027 });/*
41028  * Based on:
41029  * Ext JS Library 1.1.1
41030  * Copyright(c) 2006-2007, Ext JS, LLC.
41031  *
41032  * Originally Released Under LGPL - original licence link has changed is not relivant.
41033  *
41034  * Fork - LGPL
41035  * <script type="text/javascript">
41036  */
41037  
41038 /**
41039  * @class Roo.form.Radio
41040  * @extends Roo.form.Checkbox
41041  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41042  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41043  * @constructor
41044  * Creates a new Radio
41045  * @param {Object} config Configuration options
41046  */
41047 Roo.form.Radio = function(){
41048     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41049 };
41050 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41051     inputType: 'radio',
41052
41053     /**
41054      * If this radio is part of a group, it will return the selected value
41055      * @return {String}
41056      */
41057     getGroupValue : function(){
41058         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41059     },
41060     
41061     
41062     onRender : function(ct, position){
41063         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41064         
41065         if(this.inputValue !== undefined){
41066             this.el.dom.value = this.inputValue;
41067         }
41068          
41069         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41070         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41071         //var viewEl = this.wrap.createChild({ 
41072         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41073         //this.viewEl = viewEl;   
41074         //this.wrap.on('click', this.onClick,  this); 
41075         
41076         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41077         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41078         
41079         
41080         
41081         if(this.boxLabel){
41082             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41083         //    viewEl.on('click', this.onClick,  this); 
41084         }
41085          if(this.checked){
41086             this.el.dom.checked =   'checked' ;
41087         }
41088          
41089     } 
41090     
41091     
41092 });//<script type="text/javascript">
41093
41094 /*
41095  * Based  Ext JS Library 1.1.1
41096  * Copyright(c) 2006-2007, Ext JS, LLC.
41097  * LGPL
41098  *
41099  */
41100  
41101 /**
41102  * @class Roo.HtmlEditorCore
41103  * @extends Roo.Component
41104  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41105  *
41106  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41107  */
41108
41109 Roo.HtmlEditorCore = function(config){
41110     
41111     
41112     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41113     
41114     
41115     this.addEvents({
41116         /**
41117          * @event initialize
41118          * Fires when the editor is fully initialized (including the iframe)
41119          * @param {Roo.HtmlEditorCore} this
41120          */
41121         initialize: true,
41122         /**
41123          * @event activate
41124          * Fires when the editor is first receives the focus. Any insertion must wait
41125          * until after this event.
41126          * @param {Roo.HtmlEditorCore} this
41127          */
41128         activate: true,
41129          /**
41130          * @event beforesync
41131          * Fires before the textarea is updated with content from the editor iframe. Return false
41132          * to cancel the sync.
41133          * @param {Roo.HtmlEditorCore} this
41134          * @param {String} html
41135          */
41136         beforesync: true,
41137          /**
41138          * @event beforepush
41139          * Fires before the iframe editor is updated with content from the textarea. Return false
41140          * to cancel the push.
41141          * @param {Roo.HtmlEditorCore} this
41142          * @param {String} html
41143          */
41144         beforepush: true,
41145          /**
41146          * @event sync
41147          * Fires when the textarea is updated with content from the editor iframe.
41148          * @param {Roo.HtmlEditorCore} this
41149          * @param {String} html
41150          */
41151         sync: true,
41152          /**
41153          * @event push
41154          * Fires when the iframe editor is updated with content from the textarea.
41155          * @param {Roo.HtmlEditorCore} this
41156          * @param {String} html
41157          */
41158         push: true,
41159         
41160         /**
41161          * @event editorevent
41162          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41163          * @param {Roo.HtmlEditorCore} this
41164          */
41165         editorevent: true
41166         
41167     });
41168     
41169     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41170     
41171     // defaults : white / black...
41172     this.applyBlacklists();
41173     
41174     
41175     
41176 };
41177
41178
41179 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41180
41181
41182      /**
41183      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41184      */
41185     
41186     owner : false,
41187     
41188      /**
41189      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41190      *                        Roo.resizable.
41191      */
41192     resizable : false,
41193      /**
41194      * @cfg {Number} height (in pixels)
41195      */   
41196     height: 300,
41197    /**
41198      * @cfg {Number} width (in pixels)
41199      */   
41200     width: 500,
41201     
41202     /**
41203      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41204      * 
41205      */
41206     stylesheets: false,
41207     
41208     // id of frame..
41209     frameId: false,
41210     
41211     // private properties
41212     validationEvent : false,
41213     deferHeight: true,
41214     initialized : false,
41215     activated : false,
41216     sourceEditMode : false,
41217     onFocus : Roo.emptyFn,
41218     iframePad:3,
41219     hideMode:'offsets',
41220     
41221     clearUp: true,
41222     
41223     // blacklist + whitelisted elements..
41224     black: false,
41225     white: false,
41226      
41227     
41228
41229     /**
41230      * Protected method that will not generally be called directly. It
41231      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41232      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41233      */
41234     getDocMarkup : function(){
41235         // body styles..
41236         var st = '';
41237         
41238         // inherit styels from page...?? 
41239         if (this.stylesheets === false) {
41240             
41241             Roo.get(document.head).select('style').each(function(node) {
41242                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41243             });
41244             
41245             Roo.get(document.head).select('link').each(function(node) { 
41246                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41247             });
41248             
41249         } else if (!this.stylesheets.length) {
41250                 // simple..
41251                 st = '<style type="text/css">' +
41252                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41253                    '</style>';
41254         } else { 
41255             
41256         }
41257         
41258         st +=  '<style type="text/css">' +
41259             'IMG { cursor: pointer } ' +
41260         '</style>';
41261
41262         
41263         return '<html><head>' + st  +
41264             //<style type="text/css">' +
41265             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41266             //'</style>' +
41267             ' </head><body class="roo-htmleditor-body"></body></html>';
41268     },
41269
41270     // private
41271     onRender : function(ct, position)
41272     {
41273         var _t = this;
41274         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41275         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41276         
41277         
41278         this.el.dom.style.border = '0 none';
41279         this.el.dom.setAttribute('tabIndex', -1);
41280         this.el.addClass('x-hidden hide');
41281         
41282         
41283         
41284         if(Roo.isIE){ // fix IE 1px bogus margin
41285             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41286         }
41287        
41288         
41289         this.frameId = Roo.id();
41290         
41291          
41292         
41293         var iframe = this.owner.wrap.createChild({
41294             tag: 'iframe',
41295             cls: 'form-control', // bootstrap..
41296             id: this.frameId,
41297             name: this.frameId,
41298             frameBorder : 'no',
41299             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41300         }, this.el
41301         );
41302         
41303         
41304         this.iframe = iframe.dom;
41305
41306          this.assignDocWin();
41307         
41308         this.doc.designMode = 'on';
41309        
41310         this.doc.open();
41311         this.doc.write(this.getDocMarkup());
41312         this.doc.close();
41313
41314         
41315         var task = { // must defer to wait for browser to be ready
41316             run : function(){
41317                 //console.log("run task?" + this.doc.readyState);
41318                 this.assignDocWin();
41319                 if(this.doc.body || this.doc.readyState == 'complete'){
41320                     try {
41321                         this.doc.designMode="on";
41322                     } catch (e) {
41323                         return;
41324                     }
41325                     Roo.TaskMgr.stop(task);
41326                     this.initEditor.defer(10, this);
41327                 }
41328             },
41329             interval : 10,
41330             duration: 10000,
41331             scope: this
41332         };
41333         Roo.TaskMgr.start(task);
41334
41335     },
41336
41337     // private
41338     onResize : function(w, h)
41339     {
41340          Roo.log('resize: ' +w + ',' + h );
41341         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41342         if(!this.iframe){
41343             return;
41344         }
41345         if(typeof w == 'number'){
41346             
41347             this.iframe.style.width = w + 'px';
41348         }
41349         if(typeof h == 'number'){
41350             
41351             this.iframe.style.height = h + 'px';
41352             if(this.doc){
41353                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41354             }
41355         }
41356         
41357     },
41358
41359     /**
41360      * Toggles the editor between standard and source edit mode.
41361      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41362      */
41363     toggleSourceEdit : function(sourceEditMode){
41364         
41365         this.sourceEditMode = sourceEditMode === true;
41366         
41367         if(this.sourceEditMode){
41368  
41369             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41370             
41371         }else{
41372             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41373             //this.iframe.className = '';
41374             this.deferFocus();
41375         }
41376         //this.setSize(this.owner.wrap.getSize());
41377         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41378     },
41379
41380     
41381   
41382
41383     /**
41384      * Protected method that will not generally be called directly. If you need/want
41385      * custom HTML cleanup, this is the method you should override.
41386      * @param {String} html The HTML to be cleaned
41387      * return {String} The cleaned HTML
41388      */
41389     cleanHtml : function(html){
41390         html = String(html);
41391         if(html.length > 5){
41392             if(Roo.isSafari){ // strip safari nonsense
41393                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41394             }
41395         }
41396         if(html == '&nbsp;'){
41397             html = '';
41398         }
41399         return html;
41400     },
41401
41402     /**
41403      * HTML Editor -> Textarea
41404      * Protected method that will not generally be called directly. Syncs the contents
41405      * of the editor iframe with the textarea.
41406      */
41407     syncValue : function(){
41408         if(this.initialized){
41409             var bd = (this.doc.body || this.doc.documentElement);
41410             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41411             var html = bd.innerHTML;
41412             if(Roo.isSafari){
41413                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41414                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41415                 if(m && m[1]){
41416                     html = '<div style="'+m[0]+'">' + html + '</div>';
41417                 }
41418             }
41419             html = this.cleanHtml(html);
41420             // fix up the special chars.. normaly like back quotes in word...
41421             // however we do not want to do this with chinese..
41422             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41423                 var cc = b.charCodeAt();
41424                 if (
41425                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41426                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41427                     (cc >= 0xf900 && cc < 0xfb00 )
41428                 ) {
41429                         return b;
41430                 }
41431                 return "&#"+cc+";" 
41432             });
41433             if(this.owner.fireEvent('beforesync', this, html) !== false){
41434                 this.el.dom.value = html;
41435                 this.owner.fireEvent('sync', this, html);
41436             }
41437         }
41438     },
41439
41440     /**
41441      * Protected method that will not generally be called directly. Pushes the value of the textarea
41442      * into the iframe editor.
41443      */
41444     pushValue : function(){
41445         if(this.initialized){
41446             var v = this.el.dom.value.trim();
41447             
41448 //            if(v.length < 1){
41449 //                v = '&#160;';
41450 //            }
41451             
41452             if(this.owner.fireEvent('beforepush', this, v) !== false){
41453                 var d = (this.doc.body || this.doc.documentElement);
41454                 d.innerHTML = v;
41455                 this.cleanUpPaste();
41456                 this.el.dom.value = d.innerHTML;
41457                 this.owner.fireEvent('push', this, v);
41458             }
41459         }
41460     },
41461
41462     // private
41463     deferFocus : function(){
41464         this.focus.defer(10, this);
41465     },
41466
41467     // doc'ed in Field
41468     focus : function(){
41469         if(this.win && !this.sourceEditMode){
41470             this.win.focus();
41471         }else{
41472             this.el.focus();
41473         }
41474     },
41475     
41476     assignDocWin: function()
41477     {
41478         var iframe = this.iframe;
41479         
41480          if(Roo.isIE){
41481             this.doc = iframe.contentWindow.document;
41482             this.win = iframe.contentWindow;
41483         } else {
41484 //            if (!Roo.get(this.frameId)) {
41485 //                return;
41486 //            }
41487 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41488 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41489             
41490             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41491                 return;
41492             }
41493             
41494             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41495             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41496         }
41497     },
41498     
41499     // private
41500     initEditor : function(){
41501         //console.log("INIT EDITOR");
41502         this.assignDocWin();
41503         
41504         
41505         
41506         this.doc.designMode="on";
41507         this.doc.open();
41508         this.doc.write(this.getDocMarkup());
41509         this.doc.close();
41510         
41511         var dbody = (this.doc.body || this.doc.documentElement);
41512         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41513         // this copies styles from the containing element into thsi one..
41514         // not sure why we need all of this..
41515         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41516         
41517         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41518         //ss['background-attachment'] = 'fixed'; // w3c
41519         dbody.bgProperties = 'fixed'; // ie
41520         //Roo.DomHelper.applyStyles(dbody, ss);
41521         Roo.EventManager.on(this.doc, {
41522             //'mousedown': this.onEditorEvent,
41523             'mouseup': this.onEditorEvent,
41524             'dblclick': this.onEditorEvent,
41525             'click': this.onEditorEvent,
41526             'keyup': this.onEditorEvent,
41527             buffer:100,
41528             scope: this
41529         });
41530         if(Roo.isGecko){
41531             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41532         }
41533         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41534             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41535         }
41536         this.initialized = true;
41537
41538         this.owner.fireEvent('initialize', this);
41539         this.pushValue();
41540     },
41541
41542     // private
41543     onDestroy : function(){
41544         
41545         
41546         
41547         if(this.rendered){
41548             
41549             //for (var i =0; i < this.toolbars.length;i++) {
41550             //    // fixme - ask toolbars for heights?
41551             //    this.toolbars[i].onDestroy();
41552            // }
41553             
41554             //this.wrap.dom.innerHTML = '';
41555             //this.wrap.remove();
41556         }
41557     },
41558
41559     // private
41560     onFirstFocus : function(){
41561         
41562         this.assignDocWin();
41563         
41564         
41565         this.activated = true;
41566          
41567     
41568         if(Roo.isGecko){ // prevent silly gecko errors
41569             this.win.focus();
41570             var s = this.win.getSelection();
41571             if(!s.focusNode || s.focusNode.nodeType != 3){
41572                 var r = s.getRangeAt(0);
41573                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41574                 r.collapse(true);
41575                 this.deferFocus();
41576             }
41577             try{
41578                 this.execCmd('useCSS', true);
41579                 this.execCmd('styleWithCSS', false);
41580             }catch(e){}
41581         }
41582         this.owner.fireEvent('activate', this);
41583     },
41584
41585     // private
41586     adjustFont: function(btn){
41587         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41588         //if(Roo.isSafari){ // safari
41589         //    adjust *= 2;
41590        // }
41591         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41592         if(Roo.isSafari){ // safari
41593             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41594             v =  (v < 10) ? 10 : v;
41595             v =  (v > 48) ? 48 : v;
41596             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41597             
41598         }
41599         
41600         
41601         v = Math.max(1, v+adjust);
41602         
41603         this.execCmd('FontSize', v  );
41604     },
41605
41606     onEditorEvent : function(e){
41607         this.owner.fireEvent('editorevent', this, e);
41608       //  this.updateToolbar();
41609         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41610     },
41611
41612     insertTag : function(tg)
41613     {
41614         // could be a bit smarter... -> wrap the current selected tRoo..
41615         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41616             
41617             range = this.createRange(this.getSelection());
41618             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41619             wrappingNode.appendChild(range.extractContents());
41620             range.insertNode(wrappingNode);
41621
41622             return;
41623             
41624             
41625             
41626         }
41627         this.execCmd("formatblock",   tg);
41628         
41629     },
41630     
41631     insertText : function(txt)
41632     {
41633         
41634         
41635         var range = this.createRange();
41636         range.deleteContents();
41637                //alert(Sender.getAttribute('label'));
41638                
41639         range.insertNode(this.doc.createTextNode(txt));
41640     } ,
41641     
41642      
41643
41644     /**
41645      * Executes a Midas editor command on the editor document and performs necessary focus and
41646      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41647      * @param {String} cmd The Midas command
41648      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41649      */
41650     relayCmd : function(cmd, value){
41651         this.win.focus();
41652         this.execCmd(cmd, value);
41653         this.owner.fireEvent('editorevent', this);
41654         //this.updateToolbar();
41655         this.owner.deferFocus();
41656     },
41657
41658     /**
41659      * Executes a Midas editor command directly on the editor document.
41660      * For visual commands, you should use {@link #relayCmd} instead.
41661      * <b>This should only be called after the editor is initialized.</b>
41662      * @param {String} cmd The Midas command
41663      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41664      */
41665     execCmd : function(cmd, value){
41666         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41667         this.syncValue();
41668     },
41669  
41670  
41671    
41672     /**
41673      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41674      * to insert tRoo.
41675      * @param {String} text | dom node.. 
41676      */
41677     insertAtCursor : function(text)
41678     {
41679         
41680         
41681         
41682         if(!this.activated){
41683             return;
41684         }
41685         /*
41686         if(Roo.isIE){
41687             this.win.focus();
41688             var r = this.doc.selection.createRange();
41689             if(r){
41690                 r.collapse(true);
41691                 r.pasteHTML(text);
41692                 this.syncValue();
41693                 this.deferFocus();
41694             
41695             }
41696             return;
41697         }
41698         */
41699         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41700             this.win.focus();
41701             
41702             
41703             // from jquery ui (MIT licenced)
41704             var range, node;
41705             var win = this.win;
41706             
41707             if (win.getSelection && win.getSelection().getRangeAt) {
41708                 range = win.getSelection().getRangeAt(0);
41709                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41710                 range.insertNode(node);
41711             } else if (win.document.selection && win.document.selection.createRange) {
41712                 // no firefox support
41713                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41714                 win.document.selection.createRange().pasteHTML(txt);
41715             } else {
41716                 // no firefox support
41717                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41718                 this.execCmd('InsertHTML', txt);
41719             } 
41720             
41721             this.syncValue();
41722             
41723             this.deferFocus();
41724         }
41725     },
41726  // private
41727     mozKeyPress : function(e){
41728         if(e.ctrlKey){
41729             var c = e.getCharCode(), cmd;
41730           
41731             if(c > 0){
41732                 c = String.fromCharCode(c).toLowerCase();
41733                 switch(c){
41734                     case 'b':
41735                         cmd = 'bold';
41736                         break;
41737                     case 'i':
41738                         cmd = 'italic';
41739                         break;
41740                     
41741                     case 'u':
41742                         cmd = 'underline';
41743                         break;
41744                     
41745                     case 'v':
41746                         this.cleanUpPaste.defer(100, this);
41747                         return;
41748                         
41749                 }
41750                 if(cmd){
41751                     this.win.focus();
41752                     this.execCmd(cmd);
41753                     this.deferFocus();
41754                     e.preventDefault();
41755                 }
41756                 
41757             }
41758         }
41759     },
41760
41761     // private
41762     fixKeys : function(){ // load time branching for fastest keydown performance
41763         if(Roo.isIE){
41764             return function(e){
41765                 var k = e.getKey(), r;
41766                 if(k == e.TAB){
41767                     e.stopEvent();
41768                     r = this.doc.selection.createRange();
41769                     if(r){
41770                         r.collapse(true);
41771                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41772                         this.deferFocus();
41773                     }
41774                     return;
41775                 }
41776                 
41777                 if(k == e.ENTER){
41778                     r = this.doc.selection.createRange();
41779                     if(r){
41780                         var target = r.parentElement();
41781                         if(!target || target.tagName.toLowerCase() != 'li'){
41782                             e.stopEvent();
41783                             r.pasteHTML('<br />');
41784                             r.collapse(false);
41785                             r.select();
41786                         }
41787                     }
41788                 }
41789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41790                     this.cleanUpPaste.defer(100, this);
41791                     return;
41792                 }
41793                 
41794                 
41795             };
41796         }else if(Roo.isOpera){
41797             return function(e){
41798                 var k = e.getKey();
41799                 if(k == e.TAB){
41800                     e.stopEvent();
41801                     this.win.focus();
41802                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41803                     this.deferFocus();
41804                 }
41805                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41806                     this.cleanUpPaste.defer(100, this);
41807                     return;
41808                 }
41809                 
41810             };
41811         }else if(Roo.isSafari){
41812             return function(e){
41813                 var k = e.getKey();
41814                 
41815                 if(k == e.TAB){
41816                     e.stopEvent();
41817                     this.execCmd('InsertText','\t');
41818                     this.deferFocus();
41819                     return;
41820                 }
41821                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41822                     this.cleanUpPaste.defer(100, this);
41823                     return;
41824                 }
41825                 
41826              };
41827         }
41828     }(),
41829     
41830     getAllAncestors: function()
41831     {
41832         var p = this.getSelectedNode();
41833         var a = [];
41834         if (!p) {
41835             a.push(p); // push blank onto stack..
41836             p = this.getParentElement();
41837         }
41838         
41839         
41840         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41841             a.push(p);
41842             p = p.parentNode;
41843         }
41844         a.push(this.doc.body);
41845         return a;
41846     },
41847     lastSel : false,
41848     lastSelNode : false,
41849     
41850     
41851     getSelection : function() 
41852     {
41853         this.assignDocWin();
41854         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41855     },
41856     
41857     getSelectedNode: function() 
41858     {
41859         // this may only work on Gecko!!!
41860         
41861         // should we cache this!!!!
41862         
41863         
41864         
41865          
41866         var range = this.createRange(this.getSelection()).cloneRange();
41867         
41868         if (Roo.isIE) {
41869             var parent = range.parentElement();
41870             while (true) {
41871                 var testRange = range.duplicate();
41872                 testRange.moveToElementText(parent);
41873                 if (testRange.inRange(range)) {
41874                     break;
41875                 }
41876                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41877                     break;
41878                 }
41879                 parent = parent.parentElement;
41880             }
41881             return parent;
41882         }
41883         
41884         // is ancestor a text element.
41885         var ac =  range.commonAncestorContainer;
41886         if (ac.nodeType == 3) {
41887             ac = ac.parentNode;
41888         }
41889         
41890         var ar = ac.childNodes;
41891          
41892         var nodes = [];
41893         var other_nodes = [];
41894         var has_other_nodes = false;
41895         for (var i=0;i<ar.length;i++) {
41896             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41897                 continue;
41898             }
41899             // fullly contained node.
41900             
41901             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41902                 nodes.push(ar[i]);
41903                 continue;
41904             }
41905             
41906             // probably selected..
41907             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41908                 other_nodes.push(ar[i]);
41909                 continue;
41910             }
41911             // outer..
41912             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41913                 continue;
41914             }
41915             
41916             
41917             has_other_nodes = true;
41918         }
41919         if (!nodes.length && other_nodes.length) {
41920             nodes= other_nodes;
41921         }
41922         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41923             return false;
41924         }
41925         
41926         return nodes[0];
41927     },
41928     createRange: function(sel)
41929     {
41930         // this has strange effects when using with 
41931         // top toolbar - not sure if it's a great idea.
41932         //this.editor.contentWindow.focus();
41933         if (typeof sel != "undefined") {
41934             try {
41935                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41936             } catch(e) {
41937                 return this.doc.createRange();
41938             }
41939         } else {
41940             return this.doc.createRange();
41941         }
41942     },
41943     getParentElement: function()
41944     {
41945         
41946         this.assignDocWin();
41947         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41948         
41949         var range = this.createRange(sel);
41950          
41951         try {
41952             var p = range.commonAncestorContainer;
41953             while (p.nodeType == 3) { // text node
41954                 p = p.parentNode;
41955             }
41956             return p;
41957         } catch (e) {
41958             return null;
41959         }
41960     
41961     },
41962     /***
41963      *
41964      * Range intersection.. the hard stuff...
41965      *  '-1' = before
41966      *  '0' = hits..
41967      *  '1' = after.
41968      *         [ -- selected range --- ]
41969      *   [fail]                        [fail]
41970      *
41971      *    basically..
41972      *      if end is before start or  hits it. fail.
41973      *      if start is after end or hits it fail.
41974      *
41975      *   if either hits (but other is outside. - then it's not 
41976      *   
41977      *    
41978      **/
41979     
41980     
41981     // @see http://www.thismuchiknow.co.uk/?p=64.
41982     rangeIntersectsNode : function(range, node)
41983     {
41984         var nodeRange = node.ownerDocument.createRange();
41985         try {
41986             nodeRange.selectNode(node);
41987         } catch (e) {
41988             nodeRange.selectNodeContents(node);
41989         }
41990     
41991         var rangeStartRange = range.cloneRange();
41992         rangeStartRange.collapse(true);
41993     
41994         var rangeEndRange = range.cloneRange();
41995         rangeEndRange.collapse(false);
41996     
41997         var nodeStartRange = nodeRange.cloneRange();
41998         nodeStartRange.collapse(true);
41999     
42000         var nodeEndRange = nodeRange.cloneRange();
42001         nodeEndRange.collapse(false);
42002     
42003         return rangeStartRange.compareBoundaryPoints(
42004                  Range.START_TO_START, nodeEndRange) == -1 &&
42005                rangeEndRange.compareBoundaryPoints(
42006                  Range.START_TO_START, nodeStartRange) == 1;
42007         
42008          
42009     },
42010     rangeCompareNode : function(range, node)
42011     {
42012         var nodeRange = node.ownerDocument.createRange();
42013         try {
42014             nodeRange.selectNode(node);
42015         } catch (e) {
42016             nodeRange.selectNodeContents(node);
42017         }
42018         
42019         
42020         range.collapse(true);
42021     
42022         nodeRange.collapse(true);
42023      
42024         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42025         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42026          
42027         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42028         
42029         var nodeIsBefore   =  ss == 1;
42030         var nodeIsAfter    = ee == -1;
42031         
42032         if (nodeIsBefore && nodeIsAfter)
42033             return 0; // outer
42034         if (!nodeIsBefore && nodeIsAfter)
42035             return 1; //right trailed.
42036         
42037         if (nodeIsBefore && !nodeIsAfter)
42038             return 2;  // left trailed.
42039         // fully contined.
42040         return 3;
42041     },
42042
42043     // private? - in a new class?
42044     cleanUpPaste :  function()
42045     {
42046         // cleans up the whole document..
42047         Roo.log('cleanuppaste');
42048         
42049         this.cleanUpChildren(this.doc.body);
42050         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42051         if (clean != this.doc.body.innerHTML) {
42052             this.doc.body.innerHTML = clean;
42053         }
42054         
42055     },
42056     
42057     cleanWordChars : function(input) {// change the chars to hex code
42058         var he = Roo.HtmlEditorCore;
42059         
42060         var output = input;
42061         Roo.each(he.swapCodes, function(sw) { 
42062             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42063             
42064             output = output.replace(swapper, sw[1]);
42065         });
42066         
42067         return output;
42068     },
42069     
42070     
42071     cleanUpChildren : function (n)
42072     {
42073         if (!n.childNodes.length) {
42074             return;
42075         }
42076         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42077            this.cleanUpChild(n.childNodes[i]);
42078         }
42079     },
42080     
42081     
42082         
42083     
42084     cleanUpChild : function (node)
42085     {
42086         var ed = this;
42087         //console.log(node);
42088         if (node.nodeName == "#text") {
42089             // clean up silly Windows -- stuff?
42090             return; 
42091         }
42092         if (node.nodeName == "#comment") {
42093             node.parentNode.removeChild(node);
42094             // clean up silly Windows -- stuff?
42095             return; 
42096         }
42097         var lcname = node.tagName.toLowerCase();
42098         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42099         // whitelist of tags..
42100         
42101         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42102             // remove node.
42103             node.parentNode.removeChild(node);
42104             return;
42105             
42106         }
42107         
42108         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42109         
42110         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42111         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42112         
42113         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42114         //    remove_keep_children = true;
42115         //}
42116         
42117         if (remove_keep_children) {
42118             this.cleanUpChildren(node);
42119             // inserts everything just before this node...
42120             while (node.childNodes.length) {
42121                 var cn = node.childNodes[0];
42122                 node.removeChild(cn);
42123                 node.parentNode.insertBefore(cn, node);
42124             }
42125             node.parentNode.removeChild(node);
42126             return;
42127         }
42128         
42129         if (!node.attributes || !node.attributes.length) {
42130             this.cleanUpChildren(node);
42131             return;
42132         }
42133         
42134         function cleanAttr(n,v)
42135         {
42136             
42137             if (v.match(/^\./) || v.match(/^\//)) {
42138                 return;
42139             }
42140             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42141                 return;
42142             }
42143             if (v.match(/^#/)) {
42144                 return;
42145             }
42146 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42147             node.removeAttribute(n);
42148             
42149         }
42150         
42151         var cwhite = this.cwhite;
42152         var cblack = this.cblack;
42153             
42154         function cleanStyle(n,v)
42155         {
42156             if (v.match(/expression/)) { //XSS?? should we even bother..
42157                 node.removeAttribute(n);
42158                 return;
42159             }
42160             
42161             var parts = v.split(/;/);
42162             var clean = [];
42163             
42164             Roo.each(parts, function(p) {
42165                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42166                 if (!p.length) {
42167                     return true;
42168                 }
42169                 var l = p.split(':').shift().replace(/\s+/g,'');
42170                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42171                 
42172                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42173 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42174                     //node.removeAttribute(n);
42175                     return true;
42176                 }
42177                 //Roo.log()
42178                 // only allow 'c whitelisted system attributes'
42179                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42180 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42181                     //node.removeAttribute(n);
42182                     return true;
42183                 }
42184                 
42185                 
42186                  
42187                 
42188                 clean.push(p);
42189                 return true;
42190             });
42191             if (clean.length) { 
42192                 node.setAttribute(n, clean.join(';'));
42193             } else {
42194                 node.removeAttribute(n);
42195             }
42196             
42197         }
42198         
42199         
42200         for (var i = node.attributes.length-1; i > -1 ; i--) {
42201             var a = node.attributes[i];
42202             //console.log(a);
42203             
42204             if (a.name.toLowerCase().substr(0,2)=='on')  {
42205                 node.removeAttribute(a.name);
42206                 continue;
42207             }
42208             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42209                 node.removeAttribute(a.name);
42210                 continue;
42211             }
42212             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42213                 cleanAttr(a.name,a.value); // fixme..
42214                 continue;
42215             }
42216             if (a.name == 'style') {
42217                 cleanStyle(a.name,a.value);
42218                 continue;
42219             }
42220             /// clean up MS crap..
42221             // tecnically this should be a list of valid class'es..
42222             
42223             
42224             if (a.name == 'class') {
42225                 if (a.value.match(/^Mso/)) {
42226                     node.className = '';
42227                 }
42228                 
42229                 if (a.value.match(/body/)) {
42230                     node.className = '';
42231                 }
42232                 continue;
42233             }
42234             
42235             // style cleanup!?
42236             // class cleanup?
42237             
42238         }
42239         
42240         
42241         this.cleanUpChildren(node);
42242         
42243         
42244     },
42245     /**
42246      * Clean up MS wordisms...
42247      */
42248     cleanWord : function(node)
42249     {
42250         var _t = this;
42251         var cleanWordChildren = function()
42252         {
42253             if (!node.childNodes.length) {
42254                 return;
42255             }
42256             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42257                _t.cleanWord(node.childNodes[i]);
42258             }
42259         }
42260         
42261         
42262         if (!node) {
42263             this.cleanWord(this.doc.body);
42264             return;
42265         }
42266         if (node.nodeName == "#text") {
42267             // clean up silly Windows -- stuff?
42268             return; 
42269         }
42270         if (node.nodeName == "#comment") {
42271             node.parentNode.removeChild(node);
42272             // clean up silly Windows -- stuff?
42273             return; 
42274         }
42275         
42276         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42277             node.parentNode.removeChild(node);
42278             return;
42279         }
42280         
42281         // remove - but keep children..
42282         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42283             while (node.childNodes.length) {
42284                 var cn = node.childNodes[0];
42285                 node.removeChild(cn);
42286                 node.parentNode.insertBefore(cn, node);
42287             }
42288             node.parentNode.removeChild(node);
42289             cleanWordChildren();
42290             return;
42291         }
42292         // clean styles
42293         if (node.className.length) {
42294             
42295             var cn = node.className.split(/\W+/);
42296             var cna = [];
42297             Roo.each(cn, function(cls) {
42298                 if (cls.match(/Mso[a-zA-Z]+/)) {
42299                     return;
42300                 }
42301                 cna.push(cls);
42302             });
42303             node.className = cna.length ? cna.join(' ') : '';
42304             if (!cna.length) {
42305                 node.removeAttribute("class");
42306             }
42307         }
42308         
42309         if (node.hasAttribute("lang")) {
42310             node.removeAttribute("lang");
42311         }
42312         
42313         if (node.hasAttribute("style")) {
42314             
42315             var styles = node.getAttribute("style").split(";");
42316             var nstyle = [];
42317             Roo.each(styles, function(s) {
42318                 if (!s.match(/:/)) {
42319                     return;
42320                 }
42321                 var kv = s.split(":");
42322                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42323                     return;
42324                 }
42325                 // what ever is left... we allow.
42326                 nstyle.push(s);
42327             });
42328             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42329             if (!nstyle.length) {
42330                 node.removeAttribute('style');
42331             }
42332         }
42333         
42334         cleanWordChildren();
42335         
42336         
42337     },
42338     domToHTML : function(currentElement, depth, nopadtext) {
42339         
42340         depth = depth || 0;
42341         nopadtext = nopadtext || false;
42342     
42343         if (!currentElement) {
42344             return this.domToHTML(this.doc.body);
42345         }
42346         
42347         //Roo.log(currentElement);
42348         var j;
42349         var allText = false;
42350         var nodeName = currentElement.nodeName;
42351         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42352         
42353         if  (nodeName == '#text') {
42354             
42355             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42356         }
42357         
42358         
42359         var ret = '';
42360         if (nodeName != 'BODY') {
42361              
42362             var i = 0;
42363             // Prints the node tagName, such as <A>, <IMG>, etc
42364             if (tagName) {
42365                 var attr = [];
42366                 for(i = 0; i < currentElement.attributes.length;i++) {
42367                     // quoting?
42368                     var aname = currentElement.attributes.item(i).name;
42369                     if (!currentElement.attributes.item(i).value.length) {
42370                         continue;
42371                     }
42372                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42373                 }
42374                 
42375                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42376             } 
42377             else {
42378                 
42379                 // eack
42380             }
42381         } else {
42382             tagName = false;
42383         }
42384         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42385             return ret;
42386         }
42387         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42388             nopadtext = true;
42389         }
42390         
42391         
42392         // Traverse the tree
42393         i = 0;
42394         var currentElementChild = currentElement.childNodes.item(i);
42395         var allText = true;
42396         var innerHTML  = '';
42397         lastnode = '';
42398         while (currentElementChild) {
42399             // Formatting code (indent the tree so it looks nice on the screen)
42400             var nopad = nopadtext;
42401             if (lastnode == 'SPAN') {
42402                 nopad  = true;
42403             }
42404             // text
42405             if  (currentElementChild.nodeName == '#text') {
42406                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42407                 toadd = nopadtext ? toadd : toadd.trim();
42408                 if (!nopad && toadd.length > 80) {
42409                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42410                 }
42411                 innerHTML  += toadd;
42412                 
42413                 i++;
42414                 currentElementChild = currentElement.childNodes.item(i);
42415                 lastNode = '';
42416                 continue;
42417             }
42418             allText = false;
42419             
42420             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42421                 
42422             // Recursively traverse the tree structure of the child node
42423             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42424             lastnode = currentElementChild.nodeName;
42425             i++;
42426             currentElementChild=currentElement.childNodes.item(i);
42427         }
42428         
42429         ret += innerHTML;
42430         
42431         if (!allText) {
42432                 // The remaining code is mostly for formatting the tree
42433             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42434         }
42435         
42436         
42437         if (tagName) {
42438             ret+= "</"+tagName+">";
42439         }
42440         return ret;
42441         
42442     },
42443         
42444     applyBlacklists : function()
42445     {
42446         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42447         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42448         
42449         this.white = [];
42450         this.black = [];
42451         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42452             if (b.indexOf(tag) > -1) {
42453                 return;
42454             }
42455             this.white.push(tag);
42456             
42457         }, this);
42458         
42459         Roo.each(w, function(tag) {
42460             if (b.indexOf(tag) > -1) {
42461                 return;
42462             }
42463             if (this.white.indexOf(tag) > -1) {
42464                 return;
42465             }
42466             this.white.push(tag);
42467             
42468         }, this);
42469         
42470         
42471         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42472             if (w.indexOf(tag) > -1) {
42473                 return;
42474             }
42475             this.black.push(tag);
42476             
42477         }, this);
42478         
42479         Roo.each(b, function(tag) {
42480             if (w.indexOf(tag) > -1) {
42481                 return;
42482             }
42483             if (this.black.indexOf(tag) > -1) {
42484                 return;
42485             }
42486             this.black.push(tag);
42487             
42488         }, this);
42489         
42490         
42491         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42492         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42493         
42494         this.cwhite = [];
42495         this.cblack = [];
42496         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42497             if (b.indexOf(tag) > -1) {
42498                 return;
42499             }
42500             this.cwhite.push(tag);
42501             
42502         }, this);
42503         
42504         Roo.each(w, function(tag) {
42505             if (b.indexOf(tag) > -1) {
42506                 return;
42507             }
42508             if (this.cwhite.indexOf(tag) > -1) {
42509                 return;
42510             }
42511             this.cwhite.push(tag);
42512             
42513         }, this);
42514         
42515         
42516         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42517             if (w.indexOf(tag) > -1) {
42518                 return;
42519             }
42520             this.cblack.push(tag);
42521             
42522         }, this);
42523         
42524         Roo.each(b, function(tag) {
42525             if (w.indexOf(tag) > -1) {
42526                 return;
42527             }
42528             if (this.cblack.indexOf(tag) > -1) {
42529                 return;
42530             }
42531             this.cblack.push(tag);
42532             
42533         }, this);
42534     },
42535     
42536     setStylesheets : function(stylesheets)
42537     {
42538         if(typeof(stylesheets) == 'string'){
42539             Roo.get(this.iframe.contentDocument.head).createChild({
42540                 tag : 'link',
42541                 rel : 'stylesheet',
42542                 type : 'text/css',
42543                 href : stylesheets
42544             });
42545             
42546             return;
42547         }
42548         var _this = this;
42549      
42550         Roo.each(stylesheets, function(s) {
42551             if(!s.length){
42552                 return;
42553             }
42554             
42555             Roo.get(_this.iframe.contentDocument.head).createChild({
42556                 tag : 'link',
42557                 rel : 'stylesheet',
42558                 type : 'text/css',
42559                 href : s
42560             });
42561         });
42562
42563         
42564     },
42565     
42566     removeStylesheets : function()
42567     {
42568         var _this = this;
42569         
42570         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42571             s.remove();
42572         });
42573     }
42574     
42575     // hide stuff that is not compatible
42576     /**
42577      * @event blur
42578      * @hide
42579      */
42580     /**
42581      * @event change
42582      * @hide
42583      */
42584     /**
42585      * @event focus
42586      * @hide
42587      */
42588     /**
42589      * @event specialkey
42590      * @hide
42591      */
42592     /**
42593      * @cfg {String} fieldClass @hide
42594      */
42595     /**
42596      * @cfg {String} focusClass @hide
42597      */
42598     /**
42599      * @cfg {String} autoCreate @hide
42600      */
42601     /**
42602      * @cfg {String} inputType @hide
42603      */
42604     /**
42605      * @cfg {String} invalidClass @hide
42606      */
42607     /**
42608      * @cfg {String} invalidText @hide
42609      */
42610     /**
42611      * @cfg {String} msgFx @hide
42612      */
42613     /**
42614      * @cfg {String} validateOnBlur @hide
42615      */
42616 });
42617
42618 Roo.HtmlEditorCore.white = [
42619         'area', 'br', 'img', 'input', 'hr', 'wbr',
42620         
42621        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42622        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42623        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42624        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42625        'table',   'ul',         'xmp', 
42626        
42627        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42628       'thead',   'tr', 
42629      
42630       'dir', 'menu', 'ol', 'ul', 'dl',
42631        
42632       'embed',  'object'
42633 ];
42634
42635
42636 Roo.HtmlEditorCore.black = [
42637     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42638         'applet', // 
42639         'base',   'basefont', 'bgsound', 'blink',  'body', 
42640         'frame',  'frameset', 'head',    'html',   'ilayer', 
42641         'iframe', 'layer',  'link',     'meta',    'object',   
42642         'script', 'style' ,'title',  'xml' // clean later..
42643 ];
42644 Roo.HtmlEditorCore.clean = [
42645     'script', 'style', 'title', 'xml'
42646 ];
42647 Roo.HtmlEditorCore.remove = [
42648     'font'
42649 ];
42650 // attributes..
42651
42652 Roo.HtmlEditorCore.ablack = [
42653     'on'
42654 ];
42655     
42656 Roo.HtmlEditorCore.aclean = [ 
42657     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42658 ];
42659
42660 // protocols..
42661 Roo.HtmlEditorCore.pwhite= [
42662         'http',  'https',  'mailto'
42663 ];
42664
42665 // white listed style attributes.
42666 Roo.HtmlEditorCore.cwhite= [
42667       //  'text-align', /// default is to allow most things..
42668       
42669          
42670 //        'font-size'//??
42671 ];
42672
42673 // black listed style attributes.
42674 Roo.HtmlEditorCore.cblack= [
42675       //  'font-size' -- this can be set by the project 
42676 ];
42677
42678
42679 Roo.HtmlEditorCore.swapCodes   =[ 
42680     [    8211, "--" ], 
42681     [    8212, "--" ], 
42682     [    8216,  "'" ],  
42683     [    8217, "'" ],  
42684     [    8220, '"' ],  
42685     [    8221, '"' ],  
42686     [    8226, "*" ],  
42687     [    8230, "..." ]
42688 ]; 
42689
42690     //<script type="text/javascript">
42691
42692 /*
42693  * Ext JS Library 1.1.1
42694  * Copyright(c) 2006-2007, Ext JS, LLC.
42695  * Licence LGPL
42696  * 
42697  */
42698  
42699  
42700 Roo.form.HtmlEditor = function(config){
42701     
42702     
42703     
42704     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42705     
42706     if (!this.toolbars) {
42707         this.toolbars = [];
42708     }
42709     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42710     
42711     
42712 };
42713
42714 /**
42715  * @class Roo.form.HtmlEditor
42716  * @extends Roo.form.Field
42717  * Provides a lightweight HTML Editor component.
42718  *
42719  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42720  * 
42721  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42722  * supported by this editor.</b><br/><br/>
42723  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42724  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42725  */
42726 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42727     /**
42728      * @cfg {Boolean} clearUp
42729      */
42730     clearUp : true,
42731       /**
42732      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42733      */
42734     toolbars : false,
42735    
42736      /**
42737      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42738      *                        Roo.resizable.
42739      */
42740     resizable : false,
42741      /**
42742      * @cfg {Number} height (in pixels)
42743      */   
42744     height: 300,
42745    /**
42746      * @cfg {Number} width (in pixels)
42747      */   
42748     width: 500,
42749     
42750     /**
42751      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42752      * 
42753      */
42754     stylesheets: false,
42755     
42756     
42757      /**
42758      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42759      * 
42760      */
42761     cblack: false,
42762     /**
42763      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42764      * 
42765      */
42766     cwhite: false,
42767     
42768      /**
42769      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42770      * 
42771      */
42772     black: false,
42773     /**
42774      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42775      * 
42776      */
42777     white: false,
42778     
42779     // id of frame..
42780     frameId: false,
42781     
42782     // private properties
42783     validationEvent : false,
42784     deferHeight: true,
42785     initialized : false,
42786     activated : false,
42787     
42788     onFocus : Roo.emptyFn,
42789     iframePad:3,
42790     hideMode:'offsets',
42791     
42792     actionMode : 'container', // defaults to hiding it...
42793     
42794     defaultAutoCreate : { // modified by initCompnoent..
42795         tag: "textarea",
42796         style:"width:500px;height:300px;",
42797         autocomplete: "new-password"
42798     },
42799
42800     // private
42801     initComponent : function(){
42802         this.addEvents({
42803             /**
42804              * @event initialize
42805              * Fires when the editor is fully initialized (including the iframe)
42806              * @param {HtmlEditor} this
42807              */
42808             initialize: true,
42809             /**
42810              * @event activate
42811              * Fires when the editor is first receives the focus. Any insertion must wait
42812              * until after this event.
42813              * @param {HtmlEditor} this
42814              */
42815             activate: true,
42816              /**
42817              * @event beforesync
42818              * Fires before the textarea is updated with content from the editor iframe. Return false
42819              * to cancel the sync.
42820              * @param {HtmlEditor} this
42821              * @param {String} html
42822              */
42823             beforesync: true,
42824              /**
42825              * @event beforepush
42826              * Fires before the iframe editor is updated with content from the textarea. Return false
42827              * to cancel the push.
42828              * @param {HtmlEditor} this
42829              * @param {String} html
42830              */
42831             beforepush: true,
42832              /**
42833              * @event sync
42834              * Fires when the textarea is updated with content from the editor iframe.
42835              * @param {HtmlEditor} this
42836              * @param {String} html
42837              */
42838             sync: true,
42839              /**
42840              * @event push
42841              * Fires when the iframe editor is updated with content from the textarea.
42842              * @param {HtmlEditor} this
42843              * @param {String} html
42844              */
42845             push: true,
42846              /**
42847              * @event editmodechange
42848              * Fires when the editor switches edit modes
42849              * @param {HtmlEditor} this
42850              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42851              */
42852             editmodechange: true,
42853             /**
42854              * @event editorevent
42855              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42856              * @param {HtmlEditor} this
42857              */
42858             editorevent: true,
42859             /**
42860              * @event firstfocus
42861              * Fires when on first focus - needed by toolbars..
42862              * @param {HtmlEditor} this
42863              */
42864             firstfocus: true,
42865             /**
42866              * @event autosave
42867              * Auto save the htmlEditor value as a file into Events
42868              * @param {HtmlEditor} this
42869              */
42870             autosave: true,
42871             /**
42872              * @event savedpreview
42873              * preview the saved version of htmlEditor
42874              * @param {HtmlEditor} this
42875              */
42876             savedpreview: true,
42877             
42878             /**
42879             * @event stylesheetsclick
42880             * Fires when press the Sytlesheets button
42881             * @param {Roo.HtmlEditorCore} this
42882             */
42883             stylesheetsclick: true
42884         });
42885         this.defaultAutoCreate =  {
42886             tag: "textarea",
42887             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42888             autocomplete: "new-password"
42889         };
42890     },
42891
42892     /**
42893      * Protected method that will not generally be called directly. It
42894      * is called when the editor creates its toolbar. Override this method if you need to
42895      * add custom toolbar buttons.
42896      * @param {HtmlEditor} editor
42897      */
42898     createToolbar : function(editor){
42899         Roo.log("create toolbars");
42900         if (!editor.toolbars || !editor.toolbars.length) {
42901             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42902         }
42903         
42904         for (var i =0 ; i < editor.toolbars.length;i++) {
42905             editor.toolbars[i] = Roo.factory(
42906                     typeof(editor.toolbars[i]) == 'string' ?
42907                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42908                 Roo.form.HtmlEditor);
42909             editor.toolbars[i].init(editor);
42910         }
42911          
42912         
42913     },
42914
42915      
42916     // private
42917     onRender : function(ct, position)
42918     {
42919         var _t = this;
42920         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42921         
42922         this.wrap = this.el.wrap({
42923             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42924         });
42925         
42926         this.editorcore.onRender(ct, position);
42927          
42928         if (this.resizable) {
42929             this.resizeEl = new Roo.Resizable(this.wrap, {
42930                 pinned : true,
42931                 wrap: true,
42932                 dynamic : true,
42933                 minHeight : this.height,
42934                 height: this.height,
42935                 handles : this.resizable,
42936                 width: this.width,
42937                 listeners : {
42938                     resize : function(r, w, h) {
42939                         _t.onResize(w,h); // -something
42940                     }
42941                 }
42942             });
42943             
42944         }
42945         this.createToolbar(this);
42946        
42947         
42948         if(!this.width){
42949             this.setSize(this.wrap.getSize());
42950         }
42951         if (this.resizeEl) {
42952             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42953             // should trigger onReize..
42954         }
42955         
42956         this.keyNav = new Roo.KeyNav(this.el, {
42957             
42958             "tab" : function(e){
42959                 e.preventDefault();
42960                 
42961                 var value = this.getValue();
42962                 
42963                 var start = this.el.dom.selectionStart;
42964                 var end = this.el.dom.selectionEnd;
42965                 
42966                 if(!e.shiftKey){
42967                     
42968                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42969                     this.el.dom.setSelectionRange(end + 1, end + 1);
42970                     return;
42971                 }
42972                 
42973                 var f = value.substring(0, start).split("\t");
42974                 
42975                 if(f.pop().length != 0){
42976                     return;
42977                 }
42978                 
42979                 this.setValue(f.join("\t") + value.substring(end));
42980                 this.el.dom.setSelectionRange(start - 1, start - 1);
42981                 
42982             },
42983             
42984             "home" : function(e){
42985                 e.preventDefault();
42986                 
42987                 var curr = this.el.dom.selectionStart;
42988                 var lines = this.getValue().split("\n");
42989                 
42990                 if(!lines.length){
42991                     return;
42992                 }
42993                 
42994                 if(e.ctrlKey){
42995                     this.el.dom.setSelectionRange(0, 0);
42996                     return;
42997                 }
42998                 
42999                 var pos = 0;
43000                 
43001                 for (var i = 0; i < lines.length;i++) {
43002                     pos += lines[i].length;
43003                     
43004                     if(i != 0){
43005                         pos += 1;
43006                     }
43007                     
43008                     if(pos < curr){
43009                         continue;
43010                     }
43011                     
43012                     pos -= lines[i].length;
43013                     
43014                     break;
43015                 }
43016                 
43017                 if(!e.shiftKey){
43018                     this.el.dom.setSelectionRange(pos, pos);
43019                     return;
43020                 }
43021                 
43022                 this.el.dom.selectionStart = pos;
43023                 this.el.dom.selectionEnd = curr;
43024             },
43025             
43026             "end" : function(e){
43027                 e.preventDefault();
43028                 
43029                 var curr = this.el.dom.selectionStart;
43030                 var lines = this.getValue().split("\n");
43031                 
43032                 if(!lines.length){
43033                     return;
43034                 }
43035                 
43036                 if(e.ctrlKey){
43037                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43038                     return;
43039                 }
43040                 
43041                 var pos = 0;
43042                 
43043                 for (var i = 0; i < lines.length;i++) {
43044                     
43045                     pos += lines[i].length;
43046                     
43047                     if(i != 0){
43048                         pos += 1;
43049                     }
43050                     
43051                     if(pos < curr){
43052                         continue;
43053                     }
43054                     
43055                     break;
43056                 }
43057                 
43058                 if(!e.shiftKey){
43059                     this.el.dom.setSelectionRange(pos, pos);
43060                     return;
43061                 }
43062                 
43063                 this.el.dom.selectionStart = curr;
43064                 this.el.dom.selectionEnd = pos;
43065             },
43066
43067             scope : this,
43068
43069             doRelay : function(foo, bar, hname){
43070                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43071             },
43072
43073             forceKeyDown: true
43074         });
43075         
43076 //        if(this.autosave && this.w){
43077 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43078 //        }
43079     },
43080
43081     // private
43082     onResize : function(w, h)
43083     {
43084         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43085         var ew = false;
43086         var eh = false;
43087         
43088         if(this.el ){
43089             if(typeof w == 'number'){
43090                 var aw = w - this.wrap.getFrameWidth('lr');
43091                 this.el.setWidth(this.adjustWidth('textarea', aw));
43092                 ew = aw;
43093             }
43094             if(typeof h == 'number'){
43095                 var tbh = 0;
43096                 for (var i =0; i < this.toolbars.length;i++) {
43097                     // fixme - ask toolbars for heights?
43098                     tbh += this.toolbars[i].tb.el.getHeight();
43099                     if (this.toolbars[i].footer) {
43100                         tbh += this.toolbars[i].footer.el.getHeight();
43101                     }
43102                 }
43103                 
43104                 
43105                 
43106                 
43107                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43108                 ah -= 5; // knock a few pixes off for look..
43109 //                Roo.log(ah);
43110                 this.el.setHeight(this.adjustWidth('textarea', ah));
43111                 var eh = ah;
43112             }
43113         }
43114         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43115         this.editorcore.onResize(ew,eh);
43116         
43117     },
43118
43119     /**
43120      * Toggles the editor between standard and source edit mode.
43121      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43122      */
43123     toggleSourceEdit : function(sourceEditMode)
43124     {
43125         this.editorcore.toggleSourceEdit(sourceEditMode);
43126         
43127         if(this.editorcore.sourceEditMode){
43128             Roo.log('editor - showing textarea');
43129             
43130 //            Roo.log('in');
43131 //            Roo.log(this.syncValue());
43132             this.editorcore.syncValue();
43133             this.el.removeClass('x-hidden');
43134             this.el.dom.removeAttribute('tabIndex');
43135             this.el.focus();
43136             
43137             for (var i = 0; i < this.toolbars.length; i++) {
43138                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43139                     this.toolbars[i].tb.hide();
43140                     this.toolbars[i].footer.hide();
43141                 }
43142             }
43143             
43144         }else{
43145             Roo.log('editor - hiding textarea');
43146 //            Roo.log('out')
43147 //            Roo.log(this.pushValue()); 
43148             this.editorcore.pushValue();
43149             
43150             this.el.addClass('x-hidden');
43151             this.el.dom.setAttribute('tabIndex', -1);
43152             
43153             for (var i = 0; i < this.toolbars.length; i++) {
43154                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43155                     this.toolbars[i].tb.show();
43156                     this.toolbars[i].footer.show();
43157                 }
43158             }
43159             
43160             //this.deferFocus();
43161         }
43162         
43163         this.setSize(this.wrap.getSize());
43164         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43165         
43166         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43167     },
43168  
43169     // private (for BoxComponent)
43170     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43171
43172     // private (for BoxComponent)
43173     getResizeEl : function(){
43174         return this.wrap;
43175     },
43176
43177     // private (for BoxComponent)
43178     getPositionEl : function(){
43179         return this.wrap;
43180     },
43181
43182     // private
43183     initEvents : function(){
43184         this.originalValue = this.getValue();
43185     },
43186
43187     /**
43188      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43189      * @method
43190      */
43191     markInvalid : Roo.emptyFn,
43192     /**
43193      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43194      * @method
43195      */
43196     clearInvalid : Roo.emptyFn,
43197
43198     setValue : function(v){
43199         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43200         this.editorcore.pushValue();
43201     },
43202
43203      
43204     // private
43205     deferFocus : function(){
43206         this.focus.defer(10, this);
43207     },
43208
43209     // doc'ed in Field
43210     focus : function(){
43211         this.editorcore.focus();
43212         
43213     },
43214       
43215
43216     // private
43217     onDestroy : function(){
43218         
43219         
43220         
43221         if(this.rendered){
43222             
43223             for (var i =0; i < this.toolbars.length;i++) {
43224                 // fixme - ask toolbars for heights?
43225                 this.toolbars[i].onDestroy();
43226             }
43227             
43228             this.wrap.dom.innerHTML = '';
43229             this.wrap.remove();
43230         }
43231     },
43232
43233     // private
43234     onFirstFocus : function(){
43235         //Roo.log("onFirstFocus");
43236         this.editorcore.onFirstFocus();
43237          for (var i =0; i < this.toolbars.length;i++) {
43238             this.toolbars[i].onFirstFocus();
43239         }
43240         
43241     },
43242     
43243     // private
43244     syncValue : function()
43245     {
43246         this.editorcore.syncValue();
43247     },
43248     
43249     pushValue : function()
43250     {
43251         this.editorcore.pushValue();
43252     },
43253     
43254     setStylesheets : function(stylesheets)
43255     {
43256         this.editorcore.setStylesheets(stylesheets);
43257     },
43258     
43259     removeStylesheets : function()
43260     {
43261         this.editorcore.removeStylesheets();
43262     }
43263      
43264     
43265     // hide stuff that is not compatible
43266     /**
43267      * @event blur
43268      * @hide
43269      */
43270     /**
43271      * @event change
43272      * @hide
43273      */
43274     /**
43275      * @event focus
43276      * @hide
43277      */
43278     /**
43279      * @event specialkey
43280      * @hide
43281      */
43282     /**
43283      * @cfg {String} fieldClass @hide
43284      */
43285     /**
43286      * @cfg {String} focusClass @hide
43287      */
43288     /**
43289      * @cfg {String} autoCreate @hide
43290      */
43291     /**
43292      * @cfg {String} inputType @hide
43293      */
43294     /**
43295      * @cfg {String} invalidClass @hide
43296      */
43297     /**
43298      * @cfg {String} invalidText @hide
43299      */
43300     /**
43301      * @cfg {String} msgFx @hide
43302      */
43303     /**
43304      * @cfg {String} validateOnBlur @hide
43305      */
43306 });
43307  
43308     // <script type="text/javascript">
43309 /*
43310  * Based on
43311  * Ext JS Library 1.1.1
43312  * Copyright(c) 2006-2007, Ext JS, LLC.
43313  *  
43314  
43315  */
43316
43317 /**
43318  * @class Roo.form.HtmlEditorToolbar1
43319  * Basic Toolbar
43320  * 
43321  * Usage:
43322  *
43323  new Roo.form.HtmlEditor({
43324     ....
43325     toolbars : [
43326         new Roo.form.HtmlEditorToolbar1({
43327             disable : { fonts: 1 , format: 1, ..., ... , ...],
43328             btns : [ .... ]
43329         })
43330     }
43331      
43332  * 
43333  * @cfg {Object} disable List of elements to disable..
43334  * @cfg {Array} btns List of additional buttons.
43335  * 
43336  * 
43337  * NEEDS Extra CSS? 
43338  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43339  */
43340  
43341 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43342 {
43343     
43344     Roo.apply(this, config);
43345     
43346     // default disabled, based on 'good practice'..
43347     this.disable = this.disable || {};
43348     Roo.applyIf(this.disable, {
43349         fontSize : true,
43350         colors : true,
43351         specialElements : true
43352     });
43353     
43354     
43355     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43356     // dont call parent... till later.
43357 }
43358
43359 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43360     
43361     tb: false,
43362     
43363     rendered: false,
43364     
43365     editor : false,
43366     editorcore : false,
43367     /**
43368      * @cfg {Object} disable  List of toolbar elements to disable
43369          
43370      */
43371     disable : false,
43372     
43373     
43374      /**
43375      * @cfg {String} createLinkText The default text for the create link prompt
43376      */
43377     createLinkText : 'Please enter the URL for the link:',
43378     /**
43379      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43380      */
43381     defaultLinkValue : 'http:/'+'/',
43382    
43383     
43384       /**
43385      * @cfg {Array} fontFamilies An array of available font families
43386      */
43387     fontFamilies : [
43388         'Arial',
43389         'Courier New',
43390         'Tahoma',
43391         'Times New Roman',
43392         'Verdana'
43393     ],
43394     
43395     specialChars : [
43396            "&#169;",
43397           "&#174;",     
43398           "&#8482;",    
43399           "&#163;" ,    
43400          // "&#8212;",    
43401           "&#8230;",    
43402           "&#247;" ,    
43403         //  "&#225;" ,     ?? a acute?
43404            "&#8364;"    , //Euro
43405        //   "&#8220;"    ,
43406         //  "&#8221;"    ,
43407         //  "&#8226;"    ,
43408           "&#176;"  //   , // degrees
43409
43410          // "&#233;"     , // e ecute
43411          // "&#250;"     , // u ecute?
43412     ],
43413     
43414     specialElements : [
43415         {
43416             text: "Insert Table",
43417             xtype: 'MenuItem',
43418             xns : Roo.Menu,
43419             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43420                 
43421         },
43422         {    
43423             text: "Insert Image",
43424             xtype: 'MenuItem',
43425             xns : Roo.Menu,
43426             ihtml : '<img src="about:blank"/>'
43427             
43428         }
43429         
43430          
43431     ],
43432     
43433     
43434     inputElements : [ 
43435             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43436             "input:submit", "input:button", "select", "textarea", "label" ],
43437     formats : [
43438         ["p"] ,  
43439         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43440         ["pre"],[ "code"], 
43441         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43442         ['div'],['span']
43443     ],
43444     
43445     cleanStyles : [
43446         "font-size"
43447     ],
43448      /**
43449      * @cfg {String} defaultFont default font to use.
43450      */
43451     defaultFont: 'tahoma',
43452    
43453     fontSelect : false,
43454     
43455     
43456     formatCombo : false,
43457     
43458     init : function(editor)
43459     {
43460         this.editor = editor;
43461         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43462         var editorcore = this.editorcore;
43463         
43464         var _t = this;
43465         
43466         var fid = editorcore.frameId;
43467         var etb = this;
43468         function btn(id, toggle, handler){
43469             var xid = fid + '-'+ id ;
43470             return {
43471                 id : xid,
43472                 cmd : id,
43473                 cls : 'x-btn-icon x-edit-'+id,
43474                 enableToggle:toggle !== false,
43475                 scope: _t, // was editor...
43476                 handler:handler||_t.relayBtnCmd,
43477                 clickEvent:'mousedown',
43478                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43479                 tabIndex:-1
43480             };
43481         }
43482         
43483         
43484         
43485         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43486         this.tb = tb;
43487          // stop form submits
43488         tb.el.on('click', function(e){
43489             e.preventDefault(); // what does this do?
43490         });
43491
43492         if(!this.disable.font) { // && !Roo.isSafari){
43493             /* why no safari for fonts 
43494             editor.fontSelect = tb.el.createChild({
43495                 tag:'select',
43496                 tabIndex: -1,
43497                 cls:'x-font-select',
43498                 html: this.createFontOptions()
43499             });
43500             
43501             editor.fontSelect.on('change', function(){
43502                 var font = editor.fontSelect.dom.value;
43503                 editor.relayCmd('fontname', font);
43504                 editor.deferFocus();
43505             }, editor);
43506             
43507             tb.add(
43508                 editor.fontSelect.dom,
43509                 '-'
43510             );
43511             */
43512             
43513         };
43514         if(!this.disable.formats){
43515             this.formatCombo = new Roo.form.ComboBox({
43516                 store: new Roo.data.SimpleStore({
43517                     id : 'tag',
43518                     fields: ['tag'],
43519                     data : this.formats // from states.js
43520                 }),
43521                 blockFocus : true,
43522                 name : '',
43523                 //autoCreate : {tag: "div",  size: "20"},
43524                 displayField:'tag',
43525                 typeAhead: false,
43526                 mode: 'local',
43527                 editable : false,
43528                 triggerAction: 'all',
43529                 emptyText:'Add tag',
43530                 selectOnFocus:true,
43531                 width:135,
43532                 listeners : {
43533                     'select': function(c, r, i) {
43534                         editorcore.insertTag(r.get('tag'));
43535                         editor.focus();
43536                     }
43537                 }
43538
43539             });
43540             tb.addField(this.formatCombo);
43541             
43542         }
43543         
43544         if(!this.disable.format){
43545             tb.add(
43546                 btn('bold'),
43547                 btn('italic'),
43548                 btn('underline')
43549             );
43550         };
43551         if(!this.disable.fontSize){
43552             tb.add(
43553                 '-',
43554                 
43555                 
43556                 btn('increasefontsize', false, editorcore.adjustFont),
43557                 btn('decreasefontsize', false, editorcore.adjustFont)
43558             );
43559         };
43560         
43561         
43562         if(!this.disable.colors){
43563             tb.add(
43564                 '-', {
43565                     id:editorcore.frameId +'-forecolor',
43566                     cls:'x-btn-icon x-edit-forecolor',
43567                     clickEvent:'mousedown',
43568                     tooltip: this.buttonTips['forecolor'] || undefined,
43569                     tabIndex:-1,
43570                     menu : new Roo.menu.ColorMenu({
43571                         allowReselect: true,
43572                         focus: Roo.emptyFn,
43573                         value:'000000',
43574                         plain:true,
43575                         selectHandler: function(cp, color){
43576                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43577                             editor.deferFocus();
43578                         },
43579                         scope: editorcore,
43580                         clickEvent:'mousedown'
43581                     })
43582                 }, {
43583                     id:editorcore.frameId +'backcolor',
43584                     cls:'x-btn-icon x-edit-backcolor',
43585                     clickEvent:'mousedown',
43586                     tooltip: this.buttonTips['backcolor'] || undefined,
43587                     tabIndex:-1,
43588                     menu : new Roo.menu.ColorMenu({
43589                         focus: Roo.emptyFn,
43590                         value:'FFFFFF',
43591                         plain:true,
43592                         allowReselect: true,
43593                         selectHandler: function(cp, color){
43594                             if(Roo.isGecko){
43595                                 editorcore.execCmd('useCSS', false);
43596                                 editorcore.execCmd('hilitecolor', color);
43597                                 editorcore.execCmd('useCSS', true);
43598                                 editor.deferFocus();
43599                             }else{
43600                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43601                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43602                                 editor.deferFocus();
43603                             }
43604                         },
43605                         scope:editorcore,
43606                         clickEvent:'mousedown'
43607                     })
43608                 }
43609             );
43610         };
43611         // now add all the items...
43612         
43613
43614         if(!this.disable.alignments){
43615             tb.add(
43616                 '-',
43617                 btn('justifyleft'),
43618                 btn('justifycenter'),
43619                 btn('justifyright')
43620             );
43621         };
43622
43623         //if(!Roo.isSafari){
43624             if(!this.disable.links){
43625                 tb.add(
43626                     '-',
43627                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43628                 );
43629             };
43630
43631             if(!this.disable.lists){
43632                 tb.add(
43633                     '-',
43634                     btn('insertorderedlist'),
43635                     btn('insertunorderedlist')
43636                 );
43637             }
43638             if(!this.disable.sourceEdit){
43639                 tb.add(
43640                     '-',
43641                     btn('sourceedit', true, function(btn){
43642                         this.toggleSourceEdit(btn.pressed);
43643                     })
43644                 );
43645             }
43646         //}
43647         
43648         var smenu = { };
43649         // special menu.. - needs to be tidied up..
43650         if (!this.disable.special) {
43651             smenu = {
43652                 text: "&#169;",
43653                 cls: 'x-edit-none',
43654                 
43655                 menu : {
43656                     items : []
43657                 }
43658             };
43659             for (var i =0; i < this.specialChars.length; i++) {
43660                 smenu.menu.items.push({
43661                     
43662                     html: this.specialChars[i],
43663                     handler: function(a,b) {
43664                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43665                         //editor.insertAtCursor(a.html);
43666                         
43667                     },
43668                     tabIndex:-1
43669                 });
43670             }
43671             
43672             
43673             tb.add(smenu);
43674             
43675             
43676         }
43677         
43678         var cmenu = { };
43679         if (!this.disable.cleanStyles) {
43680             cmenu = {
43681                 cls: 'x-btn-icon x-btn-clear',
43682                 
43683                 menu : {
43684                     items : []
43685                 }
43686             };
43687             for (var i =0; i < this.cleanStyles.length; i++) {
43688                 cmenu.menu.items.push({
43689                     actiontype : this.cleanStyles[i],
43690                     html: 'Remove ' + this.cleanStyles[i],
43691                     handler: function(a,b) {
43692 //                        Roo.log(a);
43693 //                        Roo.log(b);
43694                         var c = Roo.get(editorcore.doc.body);
43695                         c.select('[style]').each(function(s) {
43696                             s.dom.style.removeProperty(a.actiontype);
43697                         });
43698                         editorcore.syncValue();
43699                     },
43700                     tabIndex:-1
43701                 });
43702             }
43703             cmenu.menu.items.push({
43704                 actiontype : 'word',
43705                 html: 'Remove MS Word Formating',
43706                 handler: function(a,b) {
43707                     editorcore.cleanWord();
43708                     editorcore.syncValue();
43709                 },
43710                 tabIndex:-1
43711             });
43712             
43713             cmenu.menu.items.push({
43714                 actiontype : 'all',
43715                 html: 'Remove All Styles',
43716                 handler: function(a,b) {
43717                     
43718                     var c = Roo.get(editorcore.doc.body);
43719                     c.select('[style]').each(function(s) {
43720                         s.dom.removeAttribute('style');
43721                     });
43722                     editorcore.syncValue();
43723                 },
43724                 tabIndex:-1
43725             });
43726              cmenu.menu.items.push({
43727                 actiontype : 'word',
43728                 html: 'Tidy HTML Source',
43729                 handler: function(a,b) {
43730                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43731                     editorcore.syncValue();
43732                 },
43733                 tabIndex:-1
43734             });
43735             
43736             
43737             tb.add(cmenu);
43738         }
43739          
43740         if (!this.disable.specialElements) {
43741             var semenu = {
43742                 text: "Other;",
43743                 cls: 'x-edit-none',
43744                 menu : {
43745                     items : []
43746                 }
43747             };
43748             for (var i =0; i < this.specialElements.length; i++) {
43749                 semenu.menu.items.push(
43750                     Roo.apply({ 
43751                         handler: function(a,b) {
43752                             editor.insertAtCursor(this.ihtml);
43753                         }
43754                     }, this.specialElements[i])
43755                 );
43756                     
43757             }
43758             
43759             tb.add(semenu);
43760             
43761             
43762         }
43763          
43764         
43765         if (this.btns) {
43766             for(var i =0; i< this.btns.length;i++) {
43767                 var b = Roo.factory(this.btns[i],Roo.form);
43768                 b.cls =  'x-edit-none';
43769                 
43770                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43771                     b.cls += ' x-init-enable';
43772                 }
43773                 
43774                 b.scope = editorcore;
43775                 tb.add(b);
43776             }
43777         
43778         }
43779         
43780         
43781         
43782         // disable everything...
43783         
43784         this.tb.items.each(function(item){
43785             
43786            if(
43787                 item.id != editorcore.frameId+ '-sourceedit' && 
43788                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43789             ){
43790                 
43791                 item.disable();
43792             }
43793         });
43794         this.rendered = true;
43795         
43796         // the all the btns;
43797         editor.on('editorevent', this.updateToolbar, this);
43798         // other toolbars need to implement this..
43799         //editor.on('editmodechange', this.updateToolbar, this);
43800     },
43801     
43802     
43803     relayBtnCmd : function(btn) {
43804         this.editorcore.relayCmd(btn.cmd);
43805     },
43806     // private used internally
43807     createLink : function(){
43808         Roo.log("create link?");
43809         var url = prompt(this.createLinkText, this.defaultLinkValue);
43810         if(url && url != 'http:/'+'/'){
43811             this.editorcore.relayCmd('createlink', url);
43812         }
43813     },
43814
43815     
43816     /**
43817      * Protected method that will not generally be called directly. It triggers
43818      * a toolbar update by reading the markup state of the current selection in the editor.
43819      */
43820     updateToolbar: function(){
43821
43822         if(!this.editorcore.activated){
43823             this.editor.onFirstFocus();
43824             return;
43825         }
43826
43827         var btns = this.tb.items.map, 
43828             doc = this.editorcore.doc,
43829             frameId = this.editorcore.frameId;
43830
43831         if(!this.disable.font && !Roo.isSafari){
43832             /*
43833             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43834             if(name != this.fontSelect.dom.value){
43835                 this.fontSelect.dom.value = name;
43836             }
43837             */
43838         }
43839         if(!this.disable.format){
43840             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43841             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43842             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43843         }
43844         if(!this.disable.alignments){
43845             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43846             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43847             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43848         }
43849         if(!Roo.isSafari && !this.disable.lists){
43850             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43851             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43852         }
43853         
43854         var ans = this.editorcore.getAllAncestors();
43855         if (this.formatCombo) {
43856             
43857             
43858             var store = this.formatCombo.store;
43859             this.formatCombo.setValue("");
43860             for (var i =0; i < ans.length;i++) {
43861                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43862                     // select it..
43863                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43864                     break;
43865                 }
43866             }
43867         }
43868         
43869         
43870         
43871         // hides menus... - so this cant be on a menu...
43872         Roo.menu.MenuMgr.hideAll();
43873
43874         //this.editorsyncValue();
43875     },
43876    
43877     
43878     createFontOptions : function(){
43879         var buf = [], fs = this.fontFamilies, ff, lc;
43880         
43881         
43882         
43883         for(var i = 0, len = fs.length; i< len; i++){
43884             ff = fs[i];
43885             lc = ff.toLowerCase();
43886             buf.push(
43887                 '<option value="',lc,'" style="font-family:',ff,';"',
43888                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43889                     ff,
43890                 '</option>'
43891             );
43892         }
43893         return buf.join('');
43894     },
43895     
43896     toggleSourceEdit : function(sourceEditMode){
43897         
43898         Roo.log("toolbar toogle");
43899         if(sourceEditMode === undefined){
43900             sourceEditMode = !this.sourceEditMode;
43901         }
43902         this.sourceEditMode = sourceEditMode === true;
43903         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43904         // just toggle the button?
43905         if(btn.pressed !== this.sourceEditMode){
43906             btn.toggle(this.sourceEditMode);
43907             return;
43908         }
43909         
43910         if(sourceEditMode){
43911             Roo.log("disabling buttons");
43912             this.tb.items.each(function(item){
43913                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43914                     item.disable();
43915                 }
43916             });
43917           
43918         }else{
43919             Roo.log("enabling buttons");
43920             if(this.editorcore.initialized){
43921                 this.tb.items.each(function(item){
43922                     item.enable();
43923                 });
43924             }
43925             
43926         }
43927         Roo.log("calling toggole on editor");
43928         // tell the editor that it's been pressed..
43929         this.editor.toggleSourceEdit(sourceEditMode);
43930        
43931     },
43932      /**
43933      * Object collection of toolbar tooltips for the buttons in the editor. The key
43934      * is the command id associated with that button and the value is a valid QuickTips object.
43935      * For example:
43936 <pre><code>
43937 {
43938     bold : {
43939         title: 'Bold (Ctrl+B)',
43940         text: 'Make the selected text bold.',
43941         cls: 'x-html-editor-tip'
43942     },
43943     italic : {
43944         title: 'Italic (Ctrl+I)',
43945         text: 'Make the selected text italic.',
43946         cls: 'x-html-editor-tip'
43947     },
43948     ...
43949 </code></pre>
43950     * @type Object
43951      */
43952     buttonTips : {
43953         bold : {
43954             title: 'Bold (Ctrl+B)',
43955             text: 'Make the selected text bold.',
43956             cls: 'x-html-editor-tip'
43957         },
43958         italic : {
43959             title: 'Italic (Ctrl+I)',
43960             text: 'Make the selected text italic.',
43961             cls: 'x-html-editor-tip'
43962         },
43963         underline : {
43964             title: 'Underline (Ctrl+U)',
43965             text: 'Underline the selected text.',
43966             cls: 'x-html-editor-tip'
43967         },
43968         increasefontsize : {
43969             title: 'Grow Text',
43970             text: 'Increase the font size.',
43971             cls: 'x-html-editor-tip'
43972         },
43973         decreasefontsize : {
43974             title: 'Shrink Text',
43975             text: 'Decrease the font size.',
43976             cls: 'x-html-editor-tip'
43977         },
43978         backcolor : {
43979             title: 'Text Highlight Color',
43980             text: 'Change the background color of the selected text.',
43981             cls: 'x-html-editor-tip'
43982         },
43983         forecolor : {
43984             title: 'Font Color',
43985             text: 'Change the color of the selected text.',
43986             cls: 'x-html-editor-tip'
43987         },
43988         justifyleft : {
43989             title: 'Align Text Left',
43990             text: 'Align text to the left.',
43991             cls: 'x-html-editor-tip'
43992         },
43993         justifycenter : {
43994             title: 'Center Text',
43995             text: 'Center text in the editor.',
43996             cls: 'x-html-editor-tip'
43997         },
43998         justifyright : {
43999             title: 'Align Text Right',
44000             text: 'Align text to the right.',
44001             cls: 'x-html-editor-tip'
44002         },
44003         insertunorderedlist : {
44004             title: 'Bullet List',
44005             text: 'Start a bulleted list.',
44006             cls: 'x-html-editor-tip'
44007         },
44008         insertorderedlist : {
44009             title: 'Numbered List',
44010             text: 'Start a numbered list.',
44011             cls: 'x-html-editor-tip'
44012         },
44013         createlink : {
44014             title: 'Hyperlink',
44015             text: 'Make the selected text a hyperlink.',
44016             cls: 'x-html-editor-tip'
44017         },
44018         sourceedit : {
44019             title: 'Source Edit',
44020             text: 'Switch to source editing mode.',
44021             cls: 'x-html-editor-tip'
44022         }
44023     },
44024     // private
44025     onDestroy : function(){
44026         if(this.rendered){
44027             
44028             this.tb.items.each(function(item){
44029                 if(item.menu){
44030                     item.menu.removeAll();
44031                     if(item.menu.el){
44032                         item.menu.el.destroy();
44033                     }
44034                 }
44035                 item.destroy();
44036             });
44037              
44038         }
44039     },
44040     onFirstFocus: function() {
44041         this.tb.items.each(function(item){
44042            item.enable();
44043         });
44044     }
44045 });
44046
44047
44048
44049
44050 // <script type="text/javascript">
44051 /*
44052  * Based on
44053  * Ext JS Library 1.1.1
44054  * Copyright(c) 2006-2007, Ext JS, LLC.
44055  *  
44056  
44057  */
44058
44059  
44060 /**
44061  * @class Roo.form.HtmlEditor.ToolbarContext
44062  * Context Toolbar
44063  * 
44064  * Usage:
44065  *
44066  new Roo.form.HtmlEditor({
44067     ....
44068     toolbars : [
44069         { xtype: 'ToolbarStandard', styles : {} }
44070         { xtype: 'ToolbarContext', disable : {} }
44071     ]
44072 })
44073
44074      
44075  * 
44076  * @config : {Object} disable List of elements to disable.. (not done yet.)
44077  * @config : {Object} styles  Map of styles available.
44078  * 
44079  */
44080
44081 Roo.form.HtmlEditor.ToolbarContext = function(config)
44082 {
44083     
44084     Roo.apply(this, config);
44085     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44086     // dont call parent... till later.
44087     this.styles = this.styles || {};
44088 }
44089
44090  
44091
44092 Roo.form.HtmlEditor.ToolbarContext.types = {
44093     'IMG' : {
44094         width : {
44095             title: "Width",
44096             width: 40
44097         },
44098         height:  {
44099             title: "Height",
44100             width: 40
44101         },
44102         align: {
44103             title: "Align",
44104             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44105             width : 80
44106             
44107         },
44108         border: {
44109             title: "Border",
44110             width: 40
44111         },
44112         alt: {
44113             title: "Alt",
44114             width: 120
44115         },
44116         src : {
44117             title: "Src",
44118             width: 220
44119         }
44120         
44121     },
44122     'A' : {
44123         name : {
44124             title: "Name",
44125             width: 50
44126         },
44127         target:  {
44128             title: "Target",
44129             width: 120
44130         },
44131         href:  {
44132             title: "Href",
44133             width: 220
44134         } // border?
44135         
44136     },
44137     'TABLE' : {
44138         rows : {
44139             title: "Rows",
44140             width: 20
44141         },
44142         cols : {
44143             title: "Cols",
44144             width: 20
44145         },
44146         width : {
44147             title: "Width",
44148             width: 40
44149         },
44150         height : {
44151             title: "Height",
44152             width: 40
44153         },
44154         border : {
44155             title: "Border",
44156             width: 20
44157         }
44158     },
44159     'TD' : {
44160         width : {
44161             title: "Width",
44162             width: 40
44163         },
44164         height : {
44165             title: "Height",
44166             width: 40
44167         },   
44168         align: {
44169             title: "Align",
44170             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44171             width: 80
44172         },
44173         valign: {
44174             title: "Valign",
44175             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44176             width: 80
44177         },
44178         colspan: {
44179             title: "Colspan",
44180             width: 20
44181             
44182         },
44183          'font-family'  : {
44184             title : "Font",
44185             style : 'fontFamily',
44186             displayField: 'display',
44187             optname : 'font-family',
44188             width: 140
44189         }
44190     },
44191     'INPUT' : {
44192         name : {
44193             title: "name",
44194             width: 120
44195         },
44196         value : {
44197             title: "Value",
44198             width: 120
44199         },
44200         width : {
44201             title: "Width",
44202             width: 40
44203         }
44204     },
44205     'LABEL' : {
44206         'for' : {
44207             title: "For",
44208             width: 120
44209         }
44210     },
44211     'TEXTAREA' : {
44212           name : {
44213             title: "name",
44214             width: 120
44215         },
44216         rows : {
44217             title: "Rows",
44218             width: 20
44219         },
44220         cols : {
44221             title: "Cols",
44222             width: 20
44223         }
44224     },
44225     'SELECT' : {
44226         name : {
44227             title: "name",
44228             width: 120
44229         },
44230         selectoptions : {
44231             title: "Options",
44232             width: 200
44233         }
44234     },
44235     
44236     // should we really allow this??
44237     // should this just be 
44238     'BODY' : {
44239         title : {
44240             title: "Title",
44241             width: 200,
44242             disabled : true
44243         }
44244     },
44245     'SPAN' : {
44246         'font-family'  : {
44247             title : "Font",
44248             style : 'fontFamily',
44249             displayField: 'display',
44250             optname : 'font-family',
44251             width: 140
44252         }
44253     },
44254     'DIV' : {
44255         'font-family'  : {
44256             title : "Font",
44257             style : 'fontFamily',
44258             displayField: 'display',
44259             optname : 'font-family',
44260             width: 140
44261         }
44262     },
44263      'P' : {
44264         'font-family'  : {
44265             title : "Font",
44266             style : 'fontFamily',
44267             displayField: 'display',
44268             optname : 'font-family',
44269             width: 140
44270         }
44271     },
44272     
44273     '*' : {
44274         // empty..
44275     }
44276
44277 };
44278
44279 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44280 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44281
44282 Roo.form.HtmlEditor.ToolbarContext.options = {
44283         'font-family'  : [ 
44284                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44285                 [ 'Courier New', 'Courier New'],
44286                 [ 'Tahoma', 'Tahoma'],
44287                 [ 'Times New Roman,serif', 'Times'],
44288                 [ 'Verdana','Verdana' ]
44289         ]
44290 };
44291
44292 // fixme - these need to be configurable..
44293  
44294
44295 Roo.form.HtmlEditor.ToolbarContext.types
44296
44297
44298 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44299     
44300     tb: false,
44301     
44302     rendered: false,
44303     
44304     editor : false,
44305     editorcore : false,
44306     /**
44307      * @cfg {Object} disable  List of toolbar elements to disable
44308          
44309      */
44310     disable : false,
44311     /**
44312      * @cfg {Object} styles List of styles 
44313      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44314      *
44315      * These must be defined in the page, so they get rendered correctly..
44316      * .headline { }
44317      * TD.underline { }
44318      * 
44319      */
44320     styles : false,
44321     
44322     options: false,
44323     
44324     toolbars : false,
44325     
44326     init : function(editor)
44327     {
44328         this.editor = editor;
44329         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44330         var editorcore = this.editorcore;
44331         
44332         var fid = editorcore.frameId;
44333         var etb = this;
44334         function btn(id, toggle, handler){
44335             var xid = fid + '-'+ id ;
44336             return {
44337                 id : xid,
44338                 cmd : id,
44339                 cls : 'x-btn-icon x-edit-'+id,
44340                 enableToggle:toggle !== false,
44341                 scope: editorcore, // was editor...
44342                 handler:handler||editorcore.relayBtnCmd,
44343                 clickEvent:'mousedown',
44344                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44345                 tabIndex:-1
44346             };
44347         }
44348         // create a new element.
44349         var wdiv = editor.wrap.createChild({
44350                 tag: 'div'
44351             }, editor.wrap.dom.firstChild.nextSibling, true);
44352         
44353         // can we do this more than once??
44354         
44355          // stop form submits
44356       
44357  
44358         // disable everything...
44359         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44360         this.toolbars = {};
44361            
44362         for (var i in  ty) {
44363           
44364             this.toolbars[i] = this.buildToolbar(ty[i],i);
44365         }
44366         this.tb = this.toolbars.BODY;
44367         this.tb.el.show();
44368         this.buildFooter();
44369         this.footer.show();
44370         editor.on('hide', function( ) { this.footer.hide() }, this);
44371         editor.on('show', function( ) { this.footer.show() }, this);
44372         
44373          
44374         this.rendered = true;
44375         
44376         // the all the btns;
44377         editor.on('editorevent', this.updateToolbar, this);
44378         // other toolbars need to implement this..
44379         //editor.on('editmodechange', this.updateToolbar, this);
44380     },
44381     
44382     
44383     
44384     /**
44385      * Protected method that will not generally be called directly. It triggers
44386      * a toolbar update by reading the markup state of the current selection in the editor.
44387      */
44388     updateToolbar: function(editor,ev,sel){
44389
44390         //Roo.log(ev);
44391         // capture mouse up - this is handy for selecting images..
44392         // perhaps should go somewhere else...
44393         if(!this.editorcore.activated){
44394              this.editor.onFirstFocus();
44395             return;
44396         }
44397         
44398         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44399         // selectNode - might want to handle IE?
44400         if (ev &&
44401             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44402             ev.target && ev.target.tagName == 'IMG') {
44403             // they have click on an image...
44404             // let's see if we can change the selection...
44405             sel = ev.target;
44406          
44407               var nodeRange = sel.ownerDocument.createRange();
44408             try {
44409                 nodeRange.selectNode(sel);
44410             } catch (e) {
44411                 nodeRange.selectNodeContents(sel);
44412             }
44413             //nodeRange.collapse(true);
44414             var s = this.editorcore.win.getSelection();
44415             s.removeAllRanges();
44416             s.addRange(nodeRange);
44417         }  
44418         
44419       
44420         var updateFooter = sel ? false : true;
44421         
44422         
44423         var ans = this.editorcore.getAllAncestors();
44424         
44425         // pick
44426         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44427         
44428         if (!sel) { 
44429             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44430             sel = sel ? sel : this.editorcore.doc.body;
44431             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44432             
44433         }
44434         // pick a menu that exists..
44435         var tn = sel.tagName.toUpperCase();
44436         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44437         
44438         tn = sel.tagName.toUpperCase();
44439         
44440         var lastSel = this.tb.selectedNode
44441         
44442         this.tb.selectedNode = sel;
44443         
44444         // if current menu does not match..
44445         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44446                 
44447             this.tb.el.hide();
44448             ///console.log("show: " + tn);
44449             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44450             this.tb.el.show();
44451             // update name
44452             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44453             
44454             
44455             // update attributes
44456             if (this.tb.fields) {
44457                 this.tb.fields.each(function(e) {
44458                     if (e.stylename) {
44459                         e.setValue(sel.style[e.stylename]);
44460                         return;
44461                     } 
44462                    e.setValue(sel.getAttribute(e.attrname));
44463                 });
44464             }
44465             
44466             var hasStyles = false;
44467             for(var i in this.styles) {
44468                 hasStyles = true;
44469                 break;
44470             }
44471             
44472             // update styles
44473             if (hasStyles) { 
44474                 var st = this.tb.fields.item(0);
44475                 
44476                 st.store.removeAll();
44477                
44478                 
44479                 var cn = sel.className.split(/\s+/);
44480                 
44481                 var avs = [];
44482                 if (this.styles['*']) {
44483                     
44484                     Roo.each(this.styles['*'], function(v) {
44485                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44486                     });
44487                 }
44488                 if (this.styles[tn]) { 
44489                     Roo.each(this.styles[tn], function(v) {
44490                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44491                     });
44492                 }
44493                 
44494                 st.store.loadData(avs);
44495                 st.collapse();
44496                 st.setValue(cn);
44497             }
44498             // flag our selected Node.
44499             this.tb.selectedNode = sel;
44500            
44501            
44502             Roo.menu.MenuMgr.hideAll();
44503
44504         }
44505         
44506         if (!updateFooter) {
44507             //this.footDisp.dom.innerHTML = ''; 
44508             return;
44509         }
44510         // update the footer
44511         //
44512         var html = '';
44513         
44514         this.footerEls = ans.reverse();
44515         Roo.each(this.footerEls, function(a,i) {
44516             if (!a) { return; }
44517             html += html.length ? ' &gt; '  :  '';
44518             
44519             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44520             
44521         });
44522        
44523         // 
44524         var sz = this.footDisp.up('td').getSize();
44525         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44526         this.footDisp.dom.style.marginLeft = '5px';
44527         
44528         this.footDisp.dom.style.overflow = 'hidden';
44529         
44530         this.footDisp.dom.innerHTML = html;
44531             
44532         //this.editorsyncValue();
44533     },
44534      
44535     
44536    
44537        
44538     // private
44539     onDestroy : function(){
44540         if(this.rendered){
44541             
44542             this.tb.items.each(function(item){
44543                 if(item.menu){
44544                     item.menu.removeAll();
44545                     if(item.menu.el){
44546                         item.menu.el.destroy();
44547                     }
44548                 }
44549                 item.destroy();
44550             });
44551              
44552         }
44553     },
44554     onFirstFocus: function() {
44555         // need to do this for all the toolbars..
44556         this.tb.items.each(function(item){
44557            item.enable();
44558         });
44559     },
44560     buildToolbar: function(tlist, nm)
44561     {
44562         var editor = this.editor;
44563         var editorcore = this.editorcore;
44564          // create a new element.
44565         var wdiv = editor.wrap.createChild({
44566                 tag: 'div'
44567             }, editor.wrap.dom.firstChild.nextSibling, true);
44568         
44569        
44570         var tb = new Roo.Toolbar(wdiv);
44571         // add the name..
44572         
44573         tb.add(nm+ ":&nbsp;");
44574         
44575         var styles = [];
44576         for(var i in this.styles) {
44577             styles.push(i);
44578         }
44579         
44580         // styles...
44581         if (styles && styles.length) {
44582             
44583             // this needs a multi-select checkbox...
44584             tb.addField( new Roo.form.ComboBox({
44585                 store: new Roo.data.SimpleStore({
44586                     id : 'val',
44587                     fields: ['val', 'selected'],
44588                     data : [] 
44589                 }),
44590                 name : '-roo-edit-className',
44591                 attrname : 'className',
44592                 displayField: 'val',
44593                 typeAhead: false,
44594                 mode: 'local',
44595                 editable : false,
44596                 triggerAction: 'all',
44597                 emptyText:'Select Style',
44598                 selectOnFocus:true,
44599                 width: 130,
44600                 listeners : {
44601                     'select': function(c, r, i) {
44602                         // initial support only for on class per el..
44603                         tb.selectedNode.className =  r ? r.get('val') : '';
44604                         editorcore.syncValue();
44605                     }
44606                 }
44607     
44608             }));
44609         }
44610         
44611         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44612         var tbops = tbc.options;
44613         
44614         for (var i in tlist) {
44615             
44616             var item = tlist[i];
44617             tb.add(item.title + ":&nbsp;");
44618             
44619             
44620             //optname == used so you can configure the options available..
44621             var opts = item.opts ? item.opts : false;
44622             if (item.optname) {
44623                 opts = tbops[item.optname];
44624            
44625             }
44626             
44627             if (opts) {
44628                 // opts == pulldown..
44629                 tb.addField( new Roo.form.ComboBox({
44630                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44631                         id : 'val',
44632                         fields: ['val', 'display'],
44633                         data : opts  
44634                     }),
44635                     name : '-roo-edit-' + i,
44636                     attrname : i,
44637                     stylename : item.style ? item.style : false,
44638                     displayField: item.displayField ? item.displayField : 'val',
44639                     valueField :  'val',
44640                     typeAhead: false,
44641                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44642                     editable : false,
44643                     triggerAction: 'all',
44644                     emptyText:'Select',
44645                     selectOnFocus:true,
44646                     width: item.width ? item.width  : 130,
44647                     listeners : {
44648                         'select': function(c, r, i) {
44649                             if (c.stylename) {
44650                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44651                                 return;
44652                             }
44653                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44654                         }
44655                     }
44656
44657                 }));
44658                 continue;
44659                     
44660                  
44661                 
44662                 tb.addField( new Roo.form.TextField({
44663                     name: i,
44664                     width: 100,
44665                     //allowBlank:false,
44666                     value: ''
44667                 }));
44668                 continue;
44669             }
44670             tb.addField( new Roo.form.TextField({
44671                 name: '-roo-edit-' + i,
44672                 attrname : i,
44673                 
44674                 width: item.width,
44675                 //allowBlank:true,
44676                 value: '',
44677                 listeners: {
44678                     'change' : function(f, nv, ov) {
44679                         tb.selectedNode.setAttribute(f.attrname, nv);
44680                     }
44681                 }
44682             }));
44683              
44684         }
44685         
44686         var _this = this;
44687         
44688         if(nm == 'BODY'){
44689             tb.addSeparator();
44690         
44691             tb.addButton( {
44692                 text: 'Stylesheets',
44693
44694                 listeners : {
44695                     click : function ()
44696                     {
44697                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44698                     }
44699                 }
44700             });
44701         }
44702         
44703         tb.addFill();
44704         tb.addButton( {
44705             text: 'Remove Tag',
44706     
44707             listeners : {
44708                 click : function ()
44709                 {
44710                     // remove
44711                     // undo does not work.
44712                      
44713                     var sn = tb.selectedNode;
44714                     
44715                     var pn = sn.parentNode;
44716                     
44717                     var stn =  sn.childNodes[0];
44718                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44719                     while (sn.childNodes.length) {
44720                         var node = sn.childNodes[0];
44721                         sn.removeChild(node);
44722                         //Roo.log(node);
44723                         pn.insertBefore(node, sn);
44724                         
44725                     }
44726                     pn.removeChild(sn);
44727                     var range = editorcore.createRange();
44728         
44729                     range.setStart(stn,0);
44730                     range.setEnd(en,0); //????
44731                     //range.selectNode(sel);
44732                     
44733                     
44734                     var selection = editorcore.getSelection();
44735                     selection.removeAllRanges();
44736                     selection.addRange(range);
44737                     
44738                     
44739                     
44740                     //_this.updateToolbar(null, null, pn);
44741                     _this.updateToolbar(null, null, null);
44742                     _this.footDisp.dom.innerHTML = ''; 
44743                 }
44744             }
44745             
44746                     
44747                 
44748             
44749         });
44750         
44751         
44752         tb.el.on('click', function(e){
44753             e.preventDefault(); // what does this do?
44754         });
44755         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44756         tb.el.hide();
44757         tb.name = nm;
44758         // dont need to disable them... as they will get hidden
44759         return tb;
44760          
44761         
44762     },
44763     buildFooter : function()
44764     {
44765         
44766         var fel = this.editor.wrap.createChild();
44767         this.footer = new Roo.Toolbar(fel);
44768         // toolbar has scrolly on left / right?
44769         var footDisp= new Roo.Toolbar.Fill();
44770         var _t = this;
44771         this.footer.add(
44772             {
44773                 text : '&lt;',
44774                 xtype: 'Button',
44775                 handler : function() {
44776                     _t.footDisp.scrollTo('left',0,true)
44777                 }
44778             }
44779         );
44780         this.footer.add( footDisp );
44781         this.footer.add( 
44782             {
44783                 text : '&gt;',
44784                 xtype: 'Button',
44785                 handler : function() {
44786                     // no animation..
44787                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44788                 }
44789             }
44790         );
44791         var fel = Roo.get(footDisp.el);
44792         fel.addClass('x-editor-context');
44793         this.footDispWrap = fel; 
44794         this.footDispWrap.overflow  = 'hidden';
44795         
44796         this.footDisp = fel.createChild();
44797         this.footDispWrap.on('click', this.onContextClick, this)
44798         
44799         
44800     },
44801     onContextClick : function (ev,dom)
44802     {
44803         ev.preventDefault();
44804         var  cn = dom.className;
44805         //Roo.log(cn);
44806         if (!cn.match(/x-ed-loc-/)) {
44807             return;
44808         }
44809         var n = cn.split('-').pop();
44810         var ans = this.footerEls;
44811         var sel = ans[n];
44812         
44813          // pick
44814         var range = this.editorcore.createRange();
44815         
44816         range.selectNodeContents(sel);
44817         //range.selectNode(sel);
44818         
44819         
44820         var selection = this.editorcore.getSelection();
44821         selection.removeAllRanges();
44822         selection.addRange(range);
44823         
44824         
44825         
44826         this.updateToolbar(null, null, sel);
44827         
44828         
44829     }
44830     
44831     
44832     
44833     
44834     
44835 });
44836
44837
44838
44839
44840
44841 /*
44842  * Based on:
44843  * Ext JS Library 1.1.1
44844  * Copyright(c) 2006-2007, Ext JS, LLC.
44845  *
44846  * Originally Released Under LGPL - original licence link has changed is not relivant.
44847  *
44848  * Fork - LGPL
44849  * <script type="text/javascript">
44850  */
44851  
44852 /**
44853  * @class Roo.form.BasicForm
44854  * @extends Roo.util.Observable
44855  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44856  * @constructor
44857  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44858  * @param {Object} config Configuration options
44859  */
44860 Roo.form.BasicForm = function(el, config){
44861     this.allItems = [];
44862     this.childForms = [];
44863     Roo.apply(this, config);
44864     /*
44865      * The Roo.form.Field items in this form.
44866      * @type MixedCollection
44867      */
44868      
44869      
44870     this.items = new Roo.util.MixedCollection(false, function(o){
44871         return o.id || (o.id = Roo.id());
44872     });
44873     this.addEvents({
44874         /**
44875          * @event beforeaction
44876          * Fires before any action is performed. Return false to cancel the action.
44877          * @param {Form} this
44878          * @param {Action} action The action to be performed
44879          */
44880         beforeaction: true,
44881         /**
44882          * @event actionfailed
44883          * Fires when an action fails.
44884          * @param {Form} this
44885          * @param {Action} action The action that failed
44886          */
44887         actionfailed : true,
44888         /**
44889          * @event actioncomplete
44890          * Fires when an action is completed.
44891          * @param {Form} this
44892          * @param {Action} action The action that completed
44893          */
44894         actioncomplete : true
44895     });
44896     if(el){
44897         this.initEl(el);
44898     }
44899     Roo.form.BasicForm.superclass.constructor.call(this);
44900 };
44901
44902 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44903     /**
44904      * @cfg {String} method
44905      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44906      */
44907     /**
44908      * @cfg {DataReader} reader
44909      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44910      * This is optional as there is built-in support for processing JSON.
44911      */
44912     /**
44913      * @cfg {DataReader} errorReader
44914      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44915      * This is completely optional as there is built-in support for processing JSON.
44916      */
44917     /**
44918      * @cfg {String} url
44919      * The URL to use for form actions if one isn't supplied in the action options.
44920      */
44921     /**
44922      * @cfg {Boolean} fileUpload
44923      * Set to true if this form is a file upload.
44924      */
44925      
44926     /**
44927      * @cfg {Object} baseParams
44928      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44929      */
44930      /**
44931      
44932     /**
44933      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44934      */
44935     timeout: 30,
44936
44937     // private
44938     activeAction : null,
44939
44940     /**
44941      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44942      * or setValues() data instead of when the form was first created.
44943      */
44944     trackResetOnLoad : false,
44945     
44946     
44947     /**
44948      * childForms - used for multi-tab forms
44949      * @type {Array}
44950      */
44951     childForms : false,
44952     
44953     /**
44954      * allItems - full list of fields.
44955      * @type {Array}
44956      */
44957     allItems : false,
44958     
44959     /**
44960      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44961      * element by passing it or its id or mask the form itself by passing in true.
44962      * @type Mixed
44963      */
44964     waitMsgTarget : false,
44965
44966     // private
44967     initEl : function(el){
44968         this.el = Roo.get(el);
44969         this.id = this.el.id || Roo.id();
44970         this.el.on('submit', this.onSubmit, this);
44971         this.el.addClass('x-form');
44972     },
44973
44974     // private
44975     onSubmit : function(e){
44976         e.stopEvent();
44977     },
44978
44979     /**
44980      * Returns true if client-side validation on the form is successful.
44981      * @return Boolean
44982      */
44983     isValid : function(){
44984         var valid = true;
44985         this.items.each(function(f){
44986            if(!f.validate()){
44987                valid = false;
44988            }
44989         });
44990         return valid;
44991     },
44992
44993     /**
44994      * Returns true if any fields in this form have changed since their original load.
44995      * @return Boolean
44996      */
44997     isDirty : function(){
44998         var dirty = false;
44999         this.items.each(function(f){
45000            if(f.isDirty()){
45001                dirty = true;
45002                return false;
45003            }
45004         });
45005         return dirty;
45006     },
45007
45008     /**
45009      * Performs a predefined action (submit or load) or custom actions you define on this form.
45010      * @param {String} actionName The name of the action type
45011      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45012      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45013      * accept other config options):
45014      * <pre>
45015 Property          Type             Description
45016 ----------------  ---------------  ----------------------------------------------------------------------------------
45017 url               String           The url for the action (defaults to the form's url)
45018 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45019 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45020 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45021                                    validate the form on the client (defaults to false)
45022      * </pre>
45023      * @return {BasicForm} this
45024      */
45025     doAction : function(action, options){
45026         if(typeof action == 'string'){
45027             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45028         }
45029         if(this.fireEvent('beforeaction', this, action) !== false){
45030             this.beforeAction(action);
45031             action.run.defer(100, action);
45032         }
45033         return this;
45034     },
45035
45036     /**
45037      * Shortcut to do a submit action.
45038      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45039      * @return {BasicForm} this
45040      */
45041     submit : function(options){
45042         this.doAction('submit', options);
45043         return this;
45044     },
45045
45046     /**
45047      * Shortcut to do a load action.
45048      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45049      * @return {BasicForm} this
45050      */
45051     load : function(options){
45052         this.doAction('load', options);
45053         return this;
45054     },
45055
45056     /**
45057      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45058      * @param {Record} record The record to edit
45059      * @return {BasicForm} this
45060      */
45061     updateRecord : function(record){
45062         record.beginEdit();
45063         var fs = record.fields;
45064         fs.each(function(f){
45065             var field = this.findField(f.name);
45066             if(field){
45067                 record.set(f.name, field.getValue());
45068             }
45069         }, this);
45070         record.endEdit();
45071         return this;
45072     },
45073
45074     /**
45075      * Loads an Roo.data.Record into this form.
45076      * @param {Record} record The record to load
45077      * @return {BasicForm} this
45078      */
45079     loadRecord : function(record){
45080         this.setValues(record.data);
45081         return this;
45082     },
45083
45084     // private
45085     beforeAction : function(action){
45086         var o = action.options;
45087         
45088        
45089         if(this.waitMsgTarget === true){
45090             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45091         }else if(this.waitMsgTarget){
45092             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45093             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45094         }else {
45095             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45096         }
45097          
45098     },
45099
45100     // private
45101     afterAction : function(action, success){
45102         this.activeAction = null;
45103         var o = action.options;
45104         
45105         if(this.waitMsgTarget === true){
45106             this.el.unmask();
45107         }else if(this.waitMsgTarget){
45108             this.waitMsgTarget.unmask();
45109         }else{
45110             Roo.MessageBox.updateProgress(1);
45111             Roo.MessageBox.hide();
45112         }
45113          
45114         if(success){
45115             if(o.reset){
45116                 this.reset();
45117             }
45118             Roo.callback(o.success, o.scope, [this, action]);
45119             this.fireEvent('actioncomplete', this, action);
45120             
45121         }else{
45122             
45123             // failure condition..
45124             // we have a scenario where updates need confirming.
45125             // eg. if a locking scenario exists..
45126             // we look for { errors : { needs_confirm : true }} in the response.
45127             if (
45128                 (typeof(action.result) != 'undefined')  &&
45129                 (typeof(action.result.errors) != 'undefined')  &&
45130                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45131            ){
45132                 var _t = this;
45133                 Roo.MessageBox.confirm(
45134                     "Change requires confirmation",
45135                     action.result.errorMsg,
45136                     function(r) {
45137                         if (r != 'yes') {
45138                             return;
45139                         }
45140                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45141                     }
45142                     
45143                 );
45144                 
45145                 
45146                 
45147                 return;
45148             }
45149             
45150             Roo.callback(o.failure, o.scope, [this, action]);
45151             // show an error message if no failed handler is set..
45152             if (!this.hasListener('actionfailed')) {
45153                 Roo.MessageBox.alert("Error",
45154                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45155                         action.result.errorMsg :
45156                         "Saving Failed, please check your entries or try again"
45157                 );
45158             }
45159             
45160             this.fireEvent('actionfailed', this, action);
45161         }
45162         
45163     },
45164
45165     /**
45166      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45167      * @param {String} id The value to search for
45168      * @return Field
45169      */
45170     findField : function(id){
45171         var field = this.items.get(id);
45172         if(!field){
45173             this.items.each(function(f){
45174                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45175                     field = f;
45176                     return false;
45177                 }
45178             });
45179         }
45180         return field || null;
45181     },
45182
45183     /**
45184      * Add a secondary form to this one, 
45185      * Used to provide tabbed forms. One form is primary, with hidden values 
45186      * which mirror the elements from the other forms.
45187      * 
45188      * @param {Roo.form.Form} form to add.
45189      * 
45190      */
45191     addForm : function(form)
45192     {
45193        
45194         if (this.childForms.indexOf(form) > -1) {
45195             // already added..
45196             return;
45197         }
45198         this.childForms.push(form);
45199         var n = '';
45200         Roo.each(form.allItems, function (fe) {
45201             
45202             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45203             if (this.findField(n)) { // already added..
45204                 return;
45205             }
45206             var add = new Roo.form.Hidden({
45207                 name : n
45208             });
45209             add.render(this.el);
45210             
45211             this.add( add );
45212         }, this);
45213         
45214     },
45215     /**
45216      * Mark fields in this form invalid in bulk.
45217      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45218      * @return {BasicForm} this
45219      */
45220     markInvalid : function(errors){
45221         if(errors instanceof Array){
45222             for(var i = 0, len = errors.length; i < len; i++){
45223                 var fieldError = errors[i];
45224                 var f = this.findField(fieldError.id);
45225                 if(f){
45226                     f.markInvalid(fieldError.msg);
45227                 }
45228             }
45229         }else{
45230             var field, id;
45231             for(id in errors){
45232                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45233                     field.markInvalid(errors[id]);
45234                 }
45235             }
45236         }
45237         Roo.each(this.childForms || [], function (f) {
45238             f.markInvalid(errors);
45239         });
45240         
45241         return this;
45242     },
45243
45244     /**
45245      * Set values for fields in this form in bulk.
45246      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45247      * @return {BasicForm} this
45248      */
45249     setValues : function(values){
45250         if(values instanceof Array){ // array of objects
45251             for(var i = 0, len = values.length; i < len; i++){
45252                 var v = values[i];
45253                 var f = this.findField(v.id);
45254                 if(f){
45255                     f.setValue(v.value);
45256                     if(this.trackResetOnLoad){
45257                         f.originalValue = f.getValue();
45258                     }
45259                 }
45260             }
45261         }else{ // object hash
45262             var field, id;
45263             for(id in values){
45264                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45265                     
45266                     if (field.setFromData && 
45267                         field.valueField && 
45268                         field.displayField &&
45269                         // combos' with local stores can 
45270                         // be queried via setValue()
45271                         // to set their value..
45272                         (field.store && !field.store.isLocal)
45273                         ) {
45274                         // it's a combo
45275                         var sd = { };
45276                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45277                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45278                         field.setFromData(sd);
45279                         
45280                     } else {
45281                         field.setValue(values[id]);
45282                     }
45283                     
45284                     
45285                     if(this.trackResetOnLoad){
45286                         field.originalValue = field.getValue();
45287                     }
45288                 }
45289             }
45290         }
45291          
45292         Roo.each(this.childForms || [], function (f) {
45293             f.setValues(values);
45294         });
45295                 
45296         return this;
45297     },
45298
45299     /**
45300      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45301      * they are returned as an array.
45302      * @param {Boolean} asString
45303      * @return {Object}
45304      */
45305     getValues : function(asString){
45306         if (this.childForms) {
45307             // copy values from the child forms
45308             Roo.each(this.childForms, function (f) {
45309                 this.setValues(f.getValues());
45310             }, this);
45311         }
45312         
45313         
45314         
45315         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45316         if(asString === true){
45317             return fs;
45318         }
45319         return Roo.urlDecode(fs);
45320     },
45321     
45322     /**
45323      * Returns the fields in this form as an object with key/value pairs. 
45324      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45325      * @return {Object}
45326      */
45327     getFieldValues : function(with_hidden)
45328     {
45329         if (this.childForms) {
45330             // copy values from the child forms
45331             // should this call getFieldValues - probably not as we do not currently copy
45332             // hidden fields when we generate..
45333             Roo.each(this.childForms, function (f) {
45334                 this.setValues(f.getValues());
45335             }, this);
45336         }
45337         
45338         var ret = {};
45339         this.items.each(function(f){
45340             if (!f.getName()) {
45341                 return;
45342             }
45343             var v = f.getValue();
45344             if (f.inputType =='radio') {
45345                 if (typeof(ret[f.getName()]) == 'undefined') {
45346                     ret[f.getName()] = ''; // empty..
45347                 }
45348                 
45349                 if (!f.el.dom.checked) {
45350                     return;
45351                     
45352                 }
45353                 v = f.el.dom.value;
45354                 
45355             }
45356             
45357             // not sure if this supported any more..
45358             if ((typeof(v) == 'object') && f.getRawValue) {
45359                 v = f.getRawValue() ; // dates..
45360             }
45361             // combo boxes where name != hiddenName...
45362             if (f.name != f.getName()) {
45363                 ret[f.name] = f.getRawValue();
45364             }
45365             ret[f.getName()] = v;
45366         });
45367         
45368         return ret;
45369     },
45370
45371     /**
45372      * Clears all invalid messages in this form.
45373      * @return {BasicForm} this
45374      */
45375     clearInvalid : function(){
45376         this.items.each(function(f){
45377            f.clearInvalid();
45378         });
45379         
45380         Roo.each(this.childForms || [], function (f) {
45381             f.clearInvalid();
45382         });
45383         
45384         
45385         return this;
45386     },
45387
45388     /**
45389      * Resets this form.
45390      * @return {BasicForm} this
45391      */
45392     reset : function(){
45393         this.items.each(function(f){
45394             f.reset();
45395         });
45396         
45397         Roo.each(this.childForms || [], function (f) {
45398             f.reset();
45399         });
45400        
45401         
45402         return this;
45403     },
45404
45405     /**
45406      * Add Roo.form components to this form.
45407      * @param {Field} field1
45408      * @param {Field} field2 (optional)
45409      * @param {Field} etc (optional)
45410      * @return {BasicForm} this
45411      */
45412     add : function(){
45413         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45414         return this;
45415     },
45416
45417
45418     /**
45419      * Removes a field from the items collection (does NOT remove its markup).
45420      * @param {Field} field
45421      * @return {BasicForm} this
45422      */
45423     remove : function(field){
45424         this.items.remove(field);
45425         return this;
45426     },
45427
45428     /**
45429      * Looks at the fields in this form, checks them for an id attribute,
45430      * and calls applyTo on the existing dom element with that id.
45431      * @return {BasicForm} this
45432      */
45433     render : function(){
45434         this.items.each(function(f){
45435             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45436                 f.applyTo(f.id);
45437             }
45438         });
45439         return this;
45440     },
45441
45442     /**
45443      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45444      * @param {Object} values
45445      * @return {BasicForm} this
45446      */
45447     applyToFields : function(o){
45448         this.items.each(function(f){
45449            Roo.apply(f, o);
45450         });
45451         return this;
45452     },
45453
45454     /**
45455      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45456      * @param {Object} values
45457      * @return {BasicForm} this
45458      */
45459     applyIfToFields : function(o){
45460         this.items.each(function(f){
45461            Roo.applyIf(f, o);
45462         });
45463         return this;
45464     }
45465 });
45466
45467 // back compat
45468 Roo.BasicForm = Roo.form.BasicForm;/*
45469  * Based on:
45470  * Ext JS Library 1.1.1
45471  * Copyright(c) 2006-2007, Ext JS, LLC.
45472  *
45473  * Originally Released Under LGPL - original licence link has changed is not relivant.
45474  *
45475  * Fork - LGPL
45476  * <script type="text/javascript">
45477  */
45478
45479 /**
45480  * @class Roo.form.Form
45481  * @extends Roo.form.BasicForm
45482  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45483  * @constructor
45484  * @param {Object} config Configuration options
45485  */
45486 Roo.form.Form = function(config){
45487     var xitems =  [];
45488     if (config.items) {
45489         xitems = config.items;
45490         delete config.items;
45491     }
45492    
45493     
45494     Roo.form.Form.superclass.constructor.call(this, null, config);
45495     this.url = this.url || this.action;
45496     if(!this.root){
45497         this.root = new Roo.form.Layout(Roo.applyIf({
45498             id: Roo.id()
45499         }, config));
45500     }
45501     this.active = this.root;
45502     /**
45503      * Array of all the buttons that have been added to this form via {@link addButton}
45504      * @type Array
45505      */
45506     this.buttons = [];
45507     this.allItems = [];
45508     this.addEvents({
45509         /**
45510          * @event clientvalidation
45511          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45512          * @param {Form} this
45513          * @param {Boolean} valid true if the form has passed client-side validation
45514          */
45515         clientvalidation: true,
45516         /**
45517          * @event rendered
45518          * Fires when the form is rendered
45519          * @param {Roo.form.Form} form
45520          */
45521         rendered : true
45522     });
45523     
45524     if (this.progressUrl) {
45525             // push a hidden field onto the list of fields..
45526             this.addxtype( {
45527                     xns: Roo.form, 
45528                     xtype : 'Hidden', 
45529                     name : 'UPLOAD_IDENTIFIER' 
45530             });
45531         }
45532         
45533     
45534     Roo.each(xitems, this.addxtype, this);
45535     
45536     
45537     
45538 };
45539
45540 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45541     /**
45542      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45543      */
45544     /**
45545      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45546      */
45547     /**
45548      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45549      */
45550     buttonAlign:'center',
45551
45552     /**
45553      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45554      */
45555     minButtonWidth:75,
45556
45557     /**
45558      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45559      * This property cascades to child containers if not set.
45560      */
45561     labelAlign:'left',
45562
45563     /**
45564      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45565      * fires a looping event with that state. This is required to bind buttons to the valid
45566      * state using the config value formBind:true on the button.
45567      */
45568     monitorValid : false,
45569
45570     /**
45571      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45572      */
45573     monitorPoll : 200,
45574     
45575     /**
45576      * @cfg {String} progressUrl - Url to return progress data 
45577      */
45578     
45579     progressUrl : false,
45580   
45581     /**
45582      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45583      * fields are added and the column is closed. If no fields are passed the column remains open
45584      * until end() is called.
45585      * @param {Object} config The config to pass to the column
45586      * @param {Field} field1 (optional)
45587      * @param {Field} field2 (optional)
45588      * @param {Field} etc (optional)
45589      * @return Column The column container object
45590      */
45591     column : function(c){
45592         var col = new Roo.form.Column(c);
45593         this.start(col);
45594         if(arguments.length > 1){ // duplicate code required because of Opera
45595             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45596             this.end();
45597         }
45598         return col;
45599     },
45600
45601     /**
45602      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45603      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45604      * until end() is called.
45605      * @param {Object} config The config to pass to the fieldset
45606      * @param {Field} field1 (optional)
45607      * @param {Field} field2 (optional)
45608      * @param {Field} etc (optional)
45609      * @return FieldSet The fieldset container object
45610      */
45611     fieldset : function(c){
45612         var fs = new Roo.form.FieldSet(c);
45613         this.start(fs);
45614         if(arguments.length > 1){ // duplicate code required because of Opera
45615             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45616             this.end();
45617         }
45618         return fs;
45619     },
45620
45621     /**
45622      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45623      * fields are added and the container is closed. If no fields are passed the container remains open
45624      * until end() is called.
45625      * @param {Object} config The config to pass to the Layout
45626      * @param {Field} field1 (optional)
45627      * @param {Field} field2 (optional)
45628      * @param {Field} etc (optional)
45629      * @return Layout The container object
45630      */
45631     container : function(c){
45632         var l = new Roo.form.Layout(c);
45633         this.start(l);
45634         if(arguments.length > 1){ // duplicate code required because of Opera
45635             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45636             this.end();
45637         }
45638         return l;
45639     },
45640
45641     /**
45642      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45643      * @param {Object} container A Roo.form.Layout or subclass of Layout
45644      * @return {Form} this
45645      */
45646     start : function(c){
45647         // cascade label info
45648         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45649         this.active.stack.push(c);
45650         c.ownerCt = this.active;
45651         this.active = c;
45652         return this;
45653     },
45654
45655     /**
45656      * Closes the current open container
45657      * @return {Form} this
45658      */
45659     end : function(){
45660         if(this.active == this.root){
45661             return this;
45662         }
45663         this.active = this.active.ownerCt;
45664         return this;
45665     },
45666
45667     /**
45668      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45669      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45670      * as the label of the field.
45671      * @param {Field} field1
45672      * @param {Field} field2 (optional)
45673      * @param {Field} etc. (optional)
45674      * @return {Form} this
45675      */
45676     add : function(){
45677         this.active.stack.push.apply(this.active.stack, arguments);
45678         this.allItems.push.apply(this.allItems,arguments);
45679         var r = [];
45680         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45681             if(a[i].isFormField){
45682                 r.push(a[i]);
45683             }
45684         }
45685         if(r.length > 0){
45686             Roo.form.Form.superclass.add.apply(this, r);
45687         }
45688         return this;
45689     },
45690     
45691
45692     
45693     
45694     
45695      /**
45696      * Find any element that has been added to a form, using it's ID or name
45697      * This can include framesets, columns etc. along with regular fields..
45698      * @param {String} id - id or name to find.
45699      
45700      * @return {Element} e - or false if nothing found.
45701      */
45702     findbyId : function(id)
45703     {
45704         var ret = false;
45705         if (!id) {
45706             return ret;
45707         }
45708         Roo.each(this.allItems, function(f){
45709             if (f.id == id || f.name == id ){
45710                 ret = f;
45711                 return false;
45712             }
45713         });
45714         return ret;
45715     },
45716
45717     
45718     
45719     /**
45720      * Render this form into the passed container. This should only be called once!
45721      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45722      * @return {Form} this
45723      */
45724     render : function(ct)
45725     {
45726         
45727         
45728         
45729         ct = Roo.get(ct);
45730         var o = this.autoCreate || {
45731             tag: 'form',
45732             method : this.method || 'POST',
45733             id : this.id || Roo.id()
45734         };
45735         this.initEl(ct.createChild(o));
45736
45737         this.root.render(this.el);
45738         
45739        
45740              
45741         this.items.each(function(f){
45742             f.render('x-form-el-'+f.id);
45743         });
45744
45745         if(this.buttons.length > 0){
45746             // tables are required to maintain order and for correct IE layout
45747             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45748                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45749                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45750             }}, null, true);
45751             var tr = tb.getElementsByTagName('tr')[0];
45752             for(var i = 0, len = this.buttons.length; i < len; i++) {
45753                 var b = this.buttons[i];
45754                 var td = document.createElement('td');
45755                 td.className = 'x-form-btn-td';
45756                 b.render(tr.appendChild(td));
45757             }
45758         }
45759         if(this.monitorValid){ // initialize after render
45760             this.startMonitoring();
45761         }
45762         this.fireEvent('rendered', this);
45763         return this;
45764     },
45765
45766     /**
45767      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45768      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45769      * object or a valid Roo.DomHelper element config
45770      * @param {Function} handler The function called when the button is clicked
45771      * @param {Object} scope (optional) The scope of the handler function
45772      * @return {Roo.Button}
45773      */
45774     addButton : function(config, handler, scope){
45775         var bc = {
45776             handler: handler,
45777             scope: scope,
45778             minWidth: this.minButtonWidth,
45779             hideParent:true
45780         };
45781         if(typeof config == "string"){
45782             bc.text = config;
45783         }else{
45784             Roo.apply(bc, config);
45785         }
45786         var btn = new Roo.Button(null, bc);
45787         this.buttons.push(btn);
45788         return btn;
45789     },
45790
45791      /**
45792      * Adds a series of form elements (using the xtype property as the factory method.
45793      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45794      * @param {Object} config 
45795      */
45796     
45797     addxtype : function()
45798     {
45799         var ar = Array.prototype.slice.call(arguments, 0);
45800         var ret = false;
45801         for(var i = 0; i < ar.length; i++) {
45802             if (!ar[i]) {
45803                 continue; // skip -- if this happends something invalid got sent, we 
45804                 // should ignore it, as basically that interface element will not show up
45805                 // and that should be pretty obvious!!
45806             }
45807             
45808             if (Roo.form[ar[i].xtype]) {
45809                 ar[i].form = this;
45810                 var fe = Roo.factory(ar[i], Roo.form);
45811                 if (!ret) {
45812                     ret = fe;
45813                 }
45814                 fe.form = this;
45815                 if (fe.store) {
45816                     fe.store.form = this;
45817                 }
45818                 if (fe.isLayout) {  
45819                          
45820                     this.start(fe);
45821                     this.allItems.push(fe);
45822                     if (fe.items && fe.addxtype) {
45823                         fe.addxtype.apply(fe, fe.items);
45824                         delete fe.items;
45825                     }
45826                      this.end();
45827                     continue;
45828                 }
45829                 
45830                 
45831                  
45832                 this.add(fe);
45833               //  console.log('adding ' + ar[i].xtype);
45834             }
45835             if (ar[i].xtype == 'Button') {  
45836                 //console.log('adding button');
45837                 //console.log(ar[i]);
45838                 this.addButton(ar[i]);
45839                 this.allItems.push(fe);
45840                 continue;
45841             }
45842             
45843             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45844                 alert('end is not supported on xtype any more, use items');
45845             //    this.end();
45846             //    //console.log('adding end');
45847             }
45848             
45849         }
45850         return ret;
45851     },
45852     
45853     /**
45854      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45855      * option "monitorValid"
45856      */
45857     startMonitoring : function(){
45858         if(!this.bound){
45859             this.bound = true;
45860             Roo.TaskMgr.start({
45861                 run : this.bindHandler,
45862                 interval : this.monitorPoll || 200,
45863                 scope: this
45864             });
45865         }
45866     },
45867
45868     /**
45869      * Stops monitoring of the valid state of this form
45870      */
45871     stopMonitoring : function(){
45872         this.bound = false;
45873     },
45874
45875     // private
45876     bindHandler : function(){
45877         if(!this.bound){
45878             return false; // stops binding
45879         }
45880         var valid = true;
45881         this.items.each(function(f){
45882             if(!f.isValid(true)){
45883                 valid = false;
45884                 return false;
45885             }
45886         });
45887         for(var i = 0, len = this.buttons.length; i < len; i++){
45888             var btn = this.buttons[i];
45889             if(btn.formBind === true && btn.disabled === valid){
45890                 btn.setDisabled(!valid);
45891             }
45892         }
45893         this.fireEvent('clientvalidation', this, valid);
45894     }
45895     
45896     
45897     
45898     
45899     
45900     
45901     
45902     
45903 });
45904
45905
45906 // back compat
45907 Roo.Form = Roo.form.Form;
45908 /*
45909  * Based on:
45910  * Ext JS Library 1.1.1
45911  * Copyright(c) 2006-2007, Ext JS, LLC.
45912  *
45913  * Originally Released Under LGPL - original licence link has changed is not relivant.
45914  *
45915  * Fork - LGPL
45916  * <script type="text/javascript">
45917  */
45918
45919 // as we use this in bootstrap.
45920 Roo.namespace('Roo.form');
45921  /**
45922  * @class Roo.form.Action
45923  * Internal Class used to handle form actions
45924  * @constructor
45925  * @param {Roo.form.BasicForm} el The form element or its id
45926  * @param {Object} config Configuration options
45927  */
45928
45929  
45930  
45931 // define the action interface
45932 Roo.form.Action = function(form, options){
45933     this.form = form;
45934     this.options = options || {};
45935 };
45936 /**
45937  * Client Validation Failed
45938  * @const 
45939  */
45940 Roo.form.Action.CLIENT_INVALID = 'client';
45941 /**
45942  * Server Validation Failed
45943  * @const 
45944  */
45945 Roo.form.Action.SERVER_INVALID = 'server';
45946  /**
45947  * Connect to Server Failed
45948  * @const 
45949  */
45950 Roo.form.Action.CONNECT_FAILURE = 'connect';
45951 /**
45952  * Reading Data from Server Failed
45953  * @const 
45954  */
45955 Roo.form.Action.LOAD_FAILURE = 'load';
45956
45957 Roo.form.Action.prototype = {
45958     type : 'default',
45959     failureType : undefined,
45960     response : undefined,
45961     result : undefined,
45962
45963     // interface method
45964     run : function(options){
45965
45966     },
45967
45968     // interface method
45969     success : function(response){
45970
45971     },
45972
45973     // interface method
45974     handleResponse : function(response){
45975
45976     },
45977
45978     // default connection failure
45979     failure : function(response){
45980         
45981         this.response = response;
45982         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45983         this.form.afterAction(this, false);
45984     },
45985
45986     processResponse : function(response){
45987         this.response = response;
45988         if(!response.responseText){
45989             return true;
45990         }
45991         this.result = this.handleResponse(response);
45992         return this.result;
45993     },
45994
45995     // utility functions used internally
45996     getUrl : function(appendParams){
45997         var url = this.options.url || this.form.url || this.form.el.dom.action;
45998         if(appendParams){
45999             var p = this.getParams();
46000             if(p){
46001                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46002             }
46003         }
46004         return url;
46005     },
46006
46007     getMethod : function(){
46008         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46009     },
46010
46011     getParams : function(){
46012         var bp = this.form.baseParams;
46013         var p = this.options.params;
46014         if(p){
46015             if(typeof p == "object"){
46016                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46017             }else if(typeof p == 'string' && bp){
46018                 p += '&' + Roo.urlEncode(bp);
46019             }
46020         }else if(bp){
46021             p = Roo.urlEncode(bp);
46022         }
46023         return p;
46024     },
46025
46026     createCallback : function(){
46027         return {
46028             success: this.success,
46029             failure: this.failure,
46030             scope: this,
46031             timeout: (this.form.timeout*1000),
46032             upload: this.form.fileUpload ? this.success : undefined
46033         };
46034     }
46035 };
46036
46037 Roo.form.Action.Submit = function(form, options){
46038     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46039 };
46040
46041 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46042     type : 'submit',
46043
46044     haveProgress : false,
46045     uploadComplete : false,
46046     
46047     // uploadProgress indicator.
46048     uploadProgress : function()
46049     {
46050         if (!this.form.progressUrl) {
46051             return;
46052         }
46053         
46054         if (!this.haveProgress) {
46055             Roo.MessageBox.progress("Uploading", "Uploading");
46056         }
46057         if (this.uploadComplete) {
46058            Roo.MessageBox.hide();
46059            return;
46060         }
46061         
46062         this.haveProgress = true;
46063    
46064         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46065         
46066         var c = new Roo.data.Connection();
46067         c.request({
46068             url : this.form.progressUrl,
46069             params: {
46070                 id : uid
46071             },
46072             method: 'GET',
46073             success : function(req){
46074                //console.log(data);
46075                 var rdata = false;
46076                 var edata;
46077                 try  {
46078                    rdata = Roo.decode(req.responseText)
46079                 } catch (e) {
46080                     Roo.log("Invalid data from server..");
46081                     Roo.log(edata);
46082                     return;
46083                 }
46084                 if (!rdata || !rdata.success) {
46085                     Roo.log(rdata);
46086                     Roo.MessageBox.alert(Roo.encode(rdata));
46087                     return;
46088                 }
46089                 var data = rdata.data;
46090                 
46091                 if (this.uploadComplete) {
46092                    Roo.MessageBox.hide();
46093                    return;
46094                 }
46095                    
46096                 if (data){
46097                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46098                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46099                     );
46100                 }
46101                 this.uploadProgress.defer(2000,this);
46102             },
46103        
46104             failure: function(data) {
46105                 Roo.log('progress url failed ');
46106                 Roo.log(data);
46107             },
46108             scope : this
46109         });
46110            
46111     },
46112     
46113     
46114     run : function()
46115     {
46116         // run get Values on the form, so it syncs any secondary forms.
46117         this.form.getValues();
46118         
46119         var o = this.options;
46120         var method = this.getMethod();
46121         var isPost = method == 'POST';
46122         if(o.clientValidation === false || this.form.isValid()){
46123             
46124             if (this.form.progressUrl) {
46125                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46126                     (new Date() * 1) + '' + Math.random());
46127                     
46128             } 
46129             
46130             
46131             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46132                 form:this.form.el.dom,
46133                 url:this.getUrl(!isPost),
46134                 method: method,
46135                 params:isPost ? this.getParams() : null,
46136                 isUpload: this.form.fileUpload
46137             }));
46138             
46139             this.uploadProgress();
46140
46141         }else if (o.clientValidation !== false){ // client validation failed
46142             this.failureType = Roo.form.Action.CLIENT_INVALID;
46143             this.form.afterAction(this, false);
46144         }
46145     },
46146
46147     success : function(response)
46148     {
46149         this.uploadComplete= true;
46150         if (this.haveProgress) {
46151             Roo.MessageBox.hide();
46152         }
46153         
46154         
46155         var result = this.processResponse(response);
46156         if(result === true || result.success){
46157             this.form.afterAction(this, true);
46158             return;
46159         }
46160         if(result.errors){
46161             this.form.markInvalid(result.errors);
46162             this.failureType = Roo.form.Action.SERVER_INVALID;
46163         }
46164         this.form.afterAction(this, false);
46165     },
46166     failure : function(response)
46167     {
46168         this.uploadComplete= true;
46169         if (this.haveProgress) {
46170             Roo.MessageBox.hide();
46171         }
46172         
46173         this.response = response;
46174         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46175         this.form.afterAction(this, false);
46176     },
46177     
46178     handleResponse : function(response){
46179         if(this.form.errorReader){
46180             var rs = this.form.errorReader.read(response);
46181             var errors = [];
46182             if(rs.records){
46183                 for(var i = 0, len = rs.records.length; i < len; i++) {
46184                     var r = rs.records[i];
46185                     errors[i] = r.data;
46186                 }
46187             }
46188             if(errors.length < 1){
46189                 errors = null;
46190             }
46191             return {
46192                 success : rs.success,
46193                 errors : errors
46194             };
46195         }
46196         var ret = false;
46197         try {
46198             ret = Roo.decode(response.responseText);
46199         } catch (e) {
46200             ret = {
46201                 success: false,
46202                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46203                 errors : []
46204             };
46205         }
46206         return ret;
46207         
46208     }
46209 });
46210
46211
46212 Roo.form.Action.Load = function(form, options){
46213     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46214     this.reader = this.form.reader;
46215 };
46216
46217 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46218     type : 'load',
46219
46220     run : function(){
46221         
46222         Roo.Ajax.request(Roo.apply(
46223                 this.createCallback(), {
46224                     method:this.getMethod(),
46225                     url:this.getUrl(false),
46226                     params:this.getParams()
46227         }));
46228     },
46229
46230     success : function(response){
46231         
46232         var result = this.processResponse(response);
46233         if(result === true || !result.success || !result.data){
46234             this.failureType = Roo.form.Action.LOAD_FAILURE;
46235             this.form.afterAction(this, false);
46236             return;
46237         }
46238         this.form.clearInvalid();
46239         this.form.setValues(result.data);
46240         this.form.afterAction(this, true);
46241     },
46242
46243     handleResponse : function(response){
46244         if(this.form.reader){
46245             var rs = this.form.reader.read(response);
46246             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46247             return {
46248                 success : rs.success,
46249                 data : data
46250             };
46251         }
46252         return Roo.decode(response.responseText);
46253     }
46254 });
46255
46256 Roo.form.Action.ACTION_TYPES = {
46257     'load' : Roo.form.Action.Load,
46258     'submit' : Roo.form.Action.Submit
46259 };/*
46260  * Based on:
46261  * Ext JS Library 1.1.1
46262  * Copyright(c) 2006-2007, Ext JS, LLC.
46263  *
46264  * Originally Released Under LGPL - original licence link has changed is not relivant.
46265  *
46266  * Fork - LGPL
46267  * <script type="text/javascript">
46268  */
46269  
46270 /**
46271  * @class Roo.form.Layout
46272  * @extends Roo.Component
46273  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46274  * @constructor
46275  * @param {Object} config Configuration options
46276  */
46277 Roo.form.Layout = function(config){
46278     var xitems = [];
46279     if (config.items) {
46280         xitems = config.items;
46281         delete config.items;
46282     }
46283     Roo.form.Layout.superclass.constructor.call(this, config);
46284     this.stack = [];
46285     Roo.each(xitems, this.addxtype, this);
46286      
46287 };
46288
46289 Roo.extend(Roo.form.Layout, Roo.Component, {
46290     /**
46291      * @cfg {String/Object} autoCreate
46292      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46293      */
46294     /**
46295      * @cfg {String/Object/Function} style
46296      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46297      * a function which returns such a specification.
46298      */
46299     /**
46300      * @cfg {String} labelAlign
46301      * Valid values are "left," "top" and "right" (defaults to "left")
46302      */
46303     /**
46304      * @cfg {Number} labelWidth
46305      * Fixed width in pixels of all field labels (defaults to undefined)
46306      */
46307     /**
46308      * @cfg {Boolean} clear
46309      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46310      */
46311     clear : true,
46312     /**
46313      * @cfg {String} labelSeparator
46314      * The separator to use after field labels (defaults to ':')
46315      */
46316     labelSeparator : ':',
46317     /**
46318      * @cfg {Boolean} hideLabels
46319      * True to suppress the display of field labels in this layout (defaults to false)
46320      */
46321     hideLabels : false,
46322
46323     // private
46324     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46325     
46326     isLayout : true,
46327     
46328     // private
46329     onRender : function(ct, position){
46330         if(this.el){ // from markup
46331             this.el = Roo.get(this.el);
46332         }else {  // generate
46333             var cfg = this.getAutoCreate();
46334             this.el = ct.createChild(cfg, position);
46335         }
46336         if(this.style){
46337             this.el.applyStyles(this.style);
46338         }
46339         if(this.labelAlign){
46340             this.el.addClass('x-form-label-'+this.labelAlign);
46341         }
46342         if(this.hideLabels){
46343             this.labelStyle = "display:none";
46344             this.elementStyle = "padding-left:0;";
46345         }else{
46346             if(typeof this.labelWidth == 'number'){
46347                 this.labelStyle = "width:"+this.labelWidth+"px;";
46348                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46349             }
46350             if(this.labelAlign == 'top'){
46351                 this.labelStyle = "width:auto;";
46352                 this.elementStyle = "padding-left:0;";
46353             }
46354         }
46355         var stack = this.stack;
46356         var slen = stack.length;
46357         if(slen > 0){
46358             if(!this.fieldTpl){
46359                 var t = new Roo.Template(
46360                     '<div class="x-form-item {5}">',
46361                         '<label for="{0}" style="{2}">{1}{4}</label>',
46362                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46363                         '</div>',
46364                     '</div><div class="x-form-clear-left"></div>'
46365                 );
46366                 t.disableFormats = true;
46367                 t.compile();
46368                 Roo.form.Layout.prototype.fieldTpl = t;
46369             }
46370             for(var i = 0; i < slen; i++) {
46371                 if(stack[i].isFormField){
46372                     this.renderField(stack[i]);
46373                 }else{
46374                     this.renderComponent(stack[i]);
46375                 }
46376             }
46377         }
46378         if(this.clear){
46379             this.el.createChild({cls:'x-form-clear'});
46380         }
46381     },
46382
46383     // private
46384     renderField : function(f){
46385         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46386                f.id, //0
46387                f.fieldLabel, //1
46388                f.labelStyle||this.labelStyle||'', //2
46389                this.elementStyle||'', //3
46390                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46391                f.itemCls||this.itemCls||''  //5
46392        ], true).getPrevSibling());
46393     },
46394
46395     // private
46396     renderComponent : function(c){
46397         c.render(c.isLayout ? this.el : this.el.createChild());    
46398     },
46399     /**
46400      * Adds a object form elements (using the xtype property as the factory method.)
46401      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46402      * @param {Object} config 
46403      */
46404     addxtype : function(o)
46405     {
46406         // create the lement.
46407         o.form = this.form;
46408         var fe = Roo.factory(o, Roo.form);
46409         this.form.allItems.push(fe);
46410         this.stack.push(fe);
46411         
46412         if (fe.isFormField) {
46413             this.form.items.add(fe);
46414         }
46415          
46416         return fe;
46417     }
46418 });
46419
46420 /**
46421  * @class Roo.form.Column
46422  * @extends Roo.form.Layout
46423  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46424  * @constructor
46425  * @param {Object} config Configuration options
46426  */
46427 Roo.form.Column = function(config){
46428     Roo.form.Column.superclass.constructor.call(this, config);
46429 };
46430
46431 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46432     /**
46433      * @cfg {Number/String} width
46434      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46435      */
46436     /**
46437      * @cfg {String/Object} autoCreate
46438      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46439      */
46440
46441     // private
46442     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46443
46444     // private
46445     onRender : function(ct, position){
46446         Roo.form.Column.superclass.onRender.call(this, ct, position);
46447         if(this.width){
46448             this.el.setWidth(this.width);
46449         }
46450     }
46451 });
46452
46453
46454 /**
46455  * @class Roo.form.Row
46456  * @extends Roo.form.Layout
46457  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46458  * @constructor
46459  * @param {Object} config Configuration options
46460  */
46461
46462  
46463 Roo.form.Row = function(config){
46464     Roo.form.Row.superclass.constructor.call(this, config);
46465 };
46466  
46467 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46468       /**
46469      * @cfg {Number/String} width
46470      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46471      */
46472     /**
46473      * @cfg {Number/String} height
46474      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46475      */
46476     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46477     
46478     padWidth : 20,
46479     // private
46480     onRender : function(ct, position){
46481         //console.log('row render');
46482         if(!this.rowTpl){
46483             var t = new Roo.Template(
46484                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46485                     '<label for="{0}" style="{2}">{1}{4}</label>',
46486                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46487                     '</div>',
46488                 '</div>'
46489             );
46490             t.disableFormats = true;
46491             t.compile();
46492             Roo.form.Layout.prototype.rowTpl = t;
46493         }
46494         this.fieldTpl = this.rowTpl;
46495         
46496         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46497         var labelWidth = 100;
46498         
46499         if ((this.labelAlign != 'top')) {
46500             if (typeof this.labelWidth == 'number') {
46501                 labelWidth = this.labelWidth
46502             }
46503             this.padWidth =  20 + labelWidth;
46504             
46505         }
46506         
46507         Roo.form.Column.superclass.onRender.call(this, ct, position);
46508         if(this.width){
46509             this.el.setWidth(this.width);
46510         }
46511         if(this.height){
46512             this.el.setHeight(this.height);
46513         }
46514     },
46515     
46516     // private
46517     renderField : function(f){
46518         f.fieldEl = this.fieldTpl.append(this.el, [
46519                f.id, f.fieldLabel,
46520                f.labelStyle||this.labelStyle||'',
46521                this.elementStyle||'',
46522                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46523                f.itemCls||this.itemCls||'',
46524                f.width ? f.width + this.padWidth : 160 + this.padWidth
46525        ],true);
46526     }
46527 });
46528  
46529
46530 /**
46531  * @class Roo.form.FieldSet
46532  * @extends Roo.form.Layout
46533  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46534  * @constructor
46535  * @param {Object} config Configuration options
46536  */
46537 Roo.form.FieldSet = function(config){
46538     Roo.form.FieldSet.superclass.constructor.call(this, config);
46539 };
46540
46541 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46542     /**
46543      * @cfg {String} legend
46544      * The text to display as the legend for the FieldSet (defaults to '')
46545      */
46546     /**
46547      * @cfg {String/Object} autoCreate
46548      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46549      */
46550
46551     // private
46552     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46553
46554     // private
46555     onRender : function(ct, position){
46556         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46557         if(this.legend){
46558             this.setLegend(this.legend);
46559         }
46560     },
46561
46562     // private
46563     setLegend : function(text){
46564         if(this.rendered){
46565             this.el.child('legend').update(text);
46566         }
46567     }
46568 });/*
46569  * Based on:
46570  * Ext JS Library 1.1.1
46571  * Copyright(c) 2006-2007, Ext JS, LLC.
46572  *
46573  * Originally Released Under LGPL - original licence link has changed is not relivant.
46574  *
46575  * Fork - LGPL
46576  * <script type="text/javascript">
46577  */
46578 /**
46579  * @class Roo.form.VTypes
46580  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46581  * @singleton
46582  */
46583 Roo.form.VTypes = function(){
46584     // closure these in so they are only created once.
46585     var alpha = /^[a-zA-Z_]+$/;
46586     var alphanum = /^[a-zA-Z0-9_]+$/;
46587     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46588     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46589
46590     // All these messages and functions are configurable
46591     return {
46592         /**
46593          * The function used to validate email addresses
46594          * @param {String} value The email address
46595          */
46596         'email' : function(v){
46597             return email.test(v);
46598         },
46599         /**
46600          * The error text to display when the email validation function returns false
46601          * @type String
46602          */
46603         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46604         /**
46605          * The keystroke filter mask to be applied on email input
46606          * @type RegExp
46607          */
46608         'emailMask' : /[a-z0-9_\.\-@]/i,
46609
46610         /**
46611          * The function used to validate URLs
46612          * @param {String} value The URL
46613          */
46614         'url' : function(v){
46615             return url.test(v);
46616         },
46617         /**
46618          * The error text to display when the url validation function returns false
46619          * @type String
46620          */
46621         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46622         
46623         /**
46624          * The function used to validate alpha values
46625          * @param {String} value The value
46626          */
46627         'alpha' : function(v){
46628             return alpha.test(v);
46629         },
46630         /**
46631          * The error text to display when the alpha validation function returns false
46632          * @type String
46633          */
46634         'alphaText' : 'This field should only contain letters and _',
46635         /**
46636          * The keystroke filter mask to be applied on alpha input
46637          * @type RegExp
46638          */
46639         'alphaMask' : /[a-z_]/i,
46640
46641         /**
46642          * The function used to validate alphanumeric values
46643          * @param {String} value The value
46644          */
46645         'alphanum' : function(v){
46646             return alphanum.test(v);
46647         },
46648         /**
46649          * The error text to display when the alphanumeric validation function returns false
46650          * @type String
46651          */
46652         'alphanumText' : 'This field should only contain letters, numbers and _',
46653         /**
46654          * The keystroke filter mask to be applied on alphanumeric input
46655          * @type RegExp
46656          */
46657         'alphanumMask' : /[a-z0-9_]/i
46658     };
46659 }();//<script type="text/javascript">
46660
46661 /**
46662  * @class Roo.form.FCKeditor
46663  * @extends Roo.form.TextArea
46664  * Wrapper around the FCKEditor http://www.fckeditor.net
46665  * @constructor
46666  * Creates a new FCKeditor
46667  * @param {Object} config Configuration options
46668  */
46669 Roo.form.FCKeditor = function(config){
46670     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46671     this.addEvents({
46672          /**
46673          * @event editorinit
46674          * Fired when the editor is initialized - you can add extra handlers here..
46675          * @param {FCKeditor} this
46676          * @param {Object} the FCK object.
46677          */
46678         editorinit : true
46679     });
46680     
46681     
46682 };
46683 Roo.form.FCKeditor.editors = { };
46684 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46685 {
46686     //defaultAutoCreate : {
46687     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46688     //},
46689     // private
46690     /**
46691      * @cfg {Object} fck options - see fck manual for details.
46692      */
46693     fckconfig : false,
46694     
46695     /**
46696      * @cfg {Object} fck toolbar set (Basic or Default)
46697      */
46698     toolbarSet : 'Basic',
46699     /**
46700      * @cfg {Object} fck BasePath
46701      */ 
46702     basePath : '/fckeditor/',
46703     
46704     
46705     frame : false,
46706     
46707     value : '',
46708     
46709    
46710     onRender : function(ct, position)
46711     {
46712         if(!this.el){
46713             this.defaultAutoCreate = {
46714                 tag: "textarea",
46715                 style:"width:300px;height:60px;",
46716                 autocomplete: "new-password"
46717             };
46718         }
46719         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46720         /*
46721         if(this.grow){
46722             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46723             if(this.preventScrollbars){
46724                 this.el.setStyle("overflow", "hidden");
46725             }
46726             this.el.setHeight(this.growMin);
46727         }
46728         */
46729         //console.log('onrender' + this.getId() );
46730         Roo.form.FCKeditor.editors[this.getId()] = this;
46731          
46732
46733         this.replaceTextarea() ;
46734         
46735     },
46736     
46737     getEditor : function() {
46738         return this.fckEditor;
46739     },
46740     /**
46741      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46742      * @param {Mixed} value The value to set
46743      */
46744     
46745     
46746     setValue : function(value)
46747     {
46748         //console.log('setValue: ' + value);
46749         
46750         if(typeof(value) == 'undefined') { // not sure why this is happending...
46751             return;
46752         }
46753         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46754         
46755         //if(!this.el || !this.getEditor()) {
46756         //    this.value = value;
46757             //this.setValue.defer(100,this,[value]);    
46758         //    return;
46759         //} 
46760         
46761         if(!this.getEditor()) {
46762             return;
46763         }
46764         
46765         this.getEditor().SetData(value);
46766         
46767         //
46768
46769     },
46770
46771     /**
46772      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46773      * @return {Mixed} value The field value
46774      */
46775     getValue : function()
46776     {
46777         
46778         if (this.frame && this.frame.dom.style.display == 'none') {
46779             return Roo.form.FCKeditor.superclass.getValue.call(this);
46780         }
46781         
46782         if(!this.el || !this.getEditor()) {
46783            
46784            // this.getValue.defer(100,this); 
46785             return this.value;
46786         }
46787        
46788         
46789         var value=this.getEditor().GetData();
46790         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46791         return Roo.form.FCKeditor.superclass.getValue.call(this);
46792         
46793
46794     },
46795
46796     /**
46797      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46798      * @return {Mixed} value The field value
46799      */
46800     getRawValue : function()
46801     {
46802         if (this.frame && this.frame.dom.style.display == 'none') {
46803             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46804         }
46805         
46806         if(!this.el || !this.getEditor()) {
46807             //this.getRawValue.defer(100,this); 
46808             return this.value;
46809             return;
46810         }
46811         
46812         
46813         
46814         var value=this.getEditor().GetData();
46815         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46816         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46817          
46818     },
46819     
46820     setSize : function(w,h) {
46821         
46822         
46823         
46824         //if (this.frame && this.frame.dom.style.display == 'none') {
46825         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46826         //    return;
46827         //}
46828         //if(!this.el || !this.getEditor()) {
46829         //    this.setSize.defer(100,this, [w,h]); 
46830         //    return;
46831         //}
46832         
46833         
46834         
46835         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46836         
46837         this.frame.dom.setAttribute('width', w);
46838         this.frame.dom.setAttribute('height', h);
46839         this.frame.setSize(w,h);
46840         
46841     },
46842     
46843     toggleSourceEdit : function(value) {
46844         
46845       
46846          
46847         this.el.dom.style.display = value ? '' : 'none';
46848         this.frame.dom.style.display = value ?  'none' : '';
46849         
46850     },
46851     
46852     
46853     focus: function(tag)
46854     {
46855         if (this.frame.dom.style.display == 'none') {
46856             return Roo.form.FCKeditor.superclass.focus.call(this);
46857         }
46858         if(!this.el || !this.getEditor()) {
46859             this.focus.defer(100,this, [tag]); 
46860             return;
46861         }
46862         
46863         
46864         
46865         
46866         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46867         this.getEditor().Focus();
46868         if (tgs.length) {
46869             if (!this.getEditor().Selection.GetSelection()) {
46870                 this.focus.defer(100,this, [tag]); 
46871                 return;
46872             }
46873             
46874             
46875             var r = this.getEditor().EditorDocument.createRange();
46876             r.setStart(tgs[0],0);
46877             r.setEnd(tgs[0],0);
46878             this.getEditor().Selection.GetSelection().removeAllRanges();
46879             this.getEditor().Selection.GetSelection().addRange(r);
46880             this.getEditor().Focus();
46881         }
46882         
46883     },
46884     
46885     
46886     
46887     replaceTextarea : function()
46888     {
46889         if ( document.getElementById( this.getId() + '___Frame' ) )
46890             return ;
46891         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46892         //{
46893             // We must check the elements firstly using the Id and then the name.
46894         var oTextarea = document.getElementById( this.getId() );
46895         
46896         var colElementsByName = document.getElementsByName( this.getId() ) ;
46897          
46898         oTextarea.style.display = 'none' ;
46899
46900         if ( oTextarea.tabIndex ) {            
46901             this.TabIndex = oTextarea.tabIndex ;
46902         }
46903         
46904         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46905         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46906         this.frame = Roo.get(this.getId() + '___Frame')
46907     },
46908     
46909     _getConfigHtml : function()
46910     {
46911         var sConfig = '' ;
46912
46913         for ( var o in this.fckconfig ) {
46914             sConfig += sConfig.length > 0  ? '&amp;' : '';
46915             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46916         }
46917
46918         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46919     },
46920     
46921     
46922     _getIFrameHtml : function()
46923     {
46924         var sFile = 'fckeditor.html' ;
46925         /* no idea what this is about..
46926         try
46927         {
46928             if ( (/fcksource=true/i).test( window.top.location.search ) )
46929                 sFile = 'fckeditor.original.html' ;
46930         }
46931         catch (e) { 
46932         */
46933
46934         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46935         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46936         
46937         
46938         var html = '<iframe id="' + this.getId() +
46939             '___Frame" src="' + sLink +
46940             '" width="' + this.width +
46941             '" height="' + this.height + '"' +
46942             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46943             ' frameborder="0" scrolling="no"></iframe>' ;
46944
46945         return html ;
46946     },
46947     
46948     _insertHtmlBefore : function( html, element )
46949     {
46950         if ( element.insertAdjacentHTML )       {
46951             // IE
46952             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46953         } else { // Gecko
46954             var oRange = document.createRange() ;
46955             oRange.setStartBefore( element ) ;
46956             var oFragment = oRange.createContextualFragment( html );
46957             element.parentNode.insertBefore( oFragment, element ) ;
46958         }
46959     }
46960     
46961     
46962   
46963     
46964     
46965     
46966     
46967
46968 });
46969
46970 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46971
46972 function FCKeditor_OnComplete(editorInstance){
46973     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46974     f.fckEditor = editorInstance;
46975     //console.log("loaded");
46976     f.fireEvent('editorinit', f, editorInstance);
46977
46978   
46979
46980  
46981
46982
46983
46984
46985
46986
46987
46988
46989
46990
46991
46992
46993
46994
46995
46996 //<script type="text/javascript">
46997 /**
46998  * @class Roo.form.GridField
46999  * @extends Roo.form.Field
47000  * Embed a grid (or editable grid into a form)
47001  * STATUS ALPHA
47002  * 
47003  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47004  * it needs 
47005  * xgrid.store = Roo.data.Store
47006  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47007  * xgrid.store.reader = Roo.data.JsonReader 
47008  * 
47009  * 
47010  * @constructor
47011  * Creates a new GridField
47012  * @param {Object} config Configuration options
47013  */
47014 Roo.form.GridField = function(config){
47015     Roo.form.GridField.superclass.constructor.call(this, config);
47016      
47017 };
47018
47019 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47020     /**
47021      * @cfg {Number} width  - used to restrict width of grid..
47022      */
47023     width : 100,
47024     /**
47025      * @cfg {Number} height - used to restrict height of grid..
47026      */
47027     height : 50,
47028      /**
47029      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47030          * 
47031          *}
47032      */
47033     xgrid : false, 
47034     /**
47035      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47036      * {tag: "input", type: "checkbox", autocomplete: "off"})
47037      */
47038    // defaultAutoCreate : { tag: 'div' },
47039     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47040     /**
47041      * @cfg {String} addTitle Text to include for adding a title.
47042      */
47043     addTitle : false,
47044     //
47045     onResize : function(){
47046         Roo.form.Field.superclass.onResize.apply(this, arguments);
47047     },
47048
47049     initEvents : function(){
47050         // Roo.form.Checkbox.superclass.initEvents.call(this);
47051         // has no events...
47052        
47053     },
47054
47055
47056     getResizeEl : function(){
47057         return this.wrap;
47058     },
47059
47060     getPositionEl : function(){
47061         return this.wrap;
47062     },
47063
47064     // private
47065     onRender : function(ct, position){
47066         
47067         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47068         var style = this.style;
47069         delete this.style;
47070         
47071         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47072         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47073         this.viewEl = this.wrap.createChild({ tag: 'div' });
47074         if (style) {
47075             this.viewEl.applyStyles(style);
47076         }
47077         if (this.width) {
47078             this.viewEl.setWidth(this.width);
47079         }
47080         if (this.height) {
47081             this.viewEl.setHeight(this.height);
47082         }
47083         //if(this.inputValue !== undefined){
47084         //this.setValue(this.value);
47085         
47086         
47087         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47088         
47089         
47090         this.grid.render();
47091         this.grid.getDataSource().on('remove', this.refreshValue, this);
47092         this.grid.getDataSource().on('update', this.refreshValue, this);
47093         this.grid.on('afteredit', this.refreshValue, this);
47094  
47095     },
47096      
47097     
47098     /**
47099      * Sets the value of the item. 
47100      * @param {String} either an object  or a string..
47101      */
47102     setValue : function(v){
47103         //this.value = v;
47104         v = v || []; // empty set..
47105         // this does not seem smart - it really only affects memoryproxy grids..
47106         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47107             var ds = this.grid.getDataSource();
47108             // assumes a json reader..
47109             var data = {}
47110             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47111             ds.loadData( data);
47112         }
47113         // clear selection so it does not get stale.
47114         if (this.grid.sm) { 
47115             this.grid.sm.clearSelections();
47116         }
47117         
47118         Roo.form.GridField.superclass.setValue.call(this, v);
47119         this.refreshValue();
47120         // should load data in the grid really....
47121     },
47122     
47123     // private
47124     refreshValue: function() {
47125          var val = [];
47126         this.grid.getDataSource().each(function(r) {
47127             val.push(r.data);
47128         });
47129         this.el.dom.value = Roo.encode(val);
47130     }
47131     
47132      
47133     
47134     
47135 });/*
47136  * Based on:
47137  * Ext JS Library 1.1.1
47138  * Copyright(c) 2006-2007, Ext JS, LLC.
47139  *
47140  * Originally Released Under LGPL - original licence link has changed is not relivant.
47141  *
47142  * Fork - LGPL
47143  * <script type="text/javascript">
47144  */
47145 /**
47146  * @class Roo.form.DisplayField
47147  * @extends Roo.form.Field
47148  * A generic Field to display non-editable data.
47149  * @constructor
47150  * Creates a new Display Field item.
47151  * @param {Object} config Configuration options
47152  */
47153 Roo.form.DisplayField = function(config){
47154     Roo.form.DisplayField.superclass.constructor.call(this, config);
47155     
47156 };
47157
47158 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47159     inputType:      'hidden',
47160     allowBlank:     true,
47161     readOnly:         true,
47162     
47163  
47164     /**
47165      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47166      */
47167     focusClass : undefined,
47168     /**
47169      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47170      */
47171     fieldClass: 'x-form-field',
47172     
47173      /**
47174      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47175      */
47176     valueRenderer: undefined,
47177     
47178     width: 100,
47179     /**
47180      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47181      * {tag: "input", type: "checkbox", autocomplete: "off"})
47182      */
47183      
47184  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47185
47186     onResize : function(){
47187         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47188         
47189     },
47190
47191     initEvents : function(){
47192         // Roo.form.Checkbox.superclass.initEvents.call(this);
47193         // has no events...
47194        
47195     },
47196
47197
47198     getResizeEl : function(){
47199         return this.wrap;
47200     },
47201
47202     getPositionEl : function(){
47203         return this.wrap;
47204     },
47205
47206     // private
47207     onRender : function(ct, position){
47208         
47209         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47210         //if(this.inputValue !== undefined){
47211         this.wrap = this.el.wrap();
47212         
47213         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47214         
47215         if (this.bodyStyle) {
47216             this.viewEl.applyStyles(this.bodyStyle);
47217         }
47218         //this.viewEl.setStyle('padding', '2px');
47219         
47220         this.setValue(this.value);
47221         
47222     },
47223 /*
47224     // private
47225     initValue : Roo.emptyFn,
47226
47227   */
47228
47229         // private
47230     onClick : function(){
47231         
47232     },
47233
47234     /**
47235      * Sets the checked state of the checkbox.
47236      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47237      */
47238     setValue : function(v){
47239         this.value = v;
47240         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47241         // this might be called before we have a dom element..
47242         if (!this.viewEl) {
47243             return;
47244         }
47245         this.viewEl.dom.innerHTML = html;
47246         Roo.form.DisplayField.superclass.setValue.call(this, v);
47247
47248     }
47249 });/*
47250  * 
47251  * Licence- LGPL
47252  * 
47253  */
47254
47255 /**
47256  * @class Roo.form.DayPicker
47257  * @extends Roo.form.Field
47258  * A Day picker show [M] [T] [W] ....
47259  * @constructor
47260  * Creates a new Day Picker
47261  * @param {Object} config Configuration options
47262  */
47263 Roo.form.DayPicker= function(config){
47264     Roo.form.DayPicker.superclass.constructor.call(this, config);
47265      
47266 };
47267
47268 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47269     /**
47270      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47271      */
47272     focusClass : undefined,
47273     /**
47274      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47275      */
47276     fieldClass: "x-form-field",
47277    
47278     /**
47279      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47280      * {tag: "input", type: "checkbox", autocomplete: "off"})
47281      */
47282     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47283     
47284    
47285     actionMode : 'viewEl', 
47286     //
47287     // private
47288  
47289     inputType : 'hidden',
47290     
47291      
47292     inputElement: false, // real input element?
47293     basedOn: false, // ????
47294     
47295     isFormField: true, // not sure where this is needed!!!!
47296
47297     onResize : function(){
47298         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47299         if(!this.boxLabel){
47300             this.el.alignTo(this.wrap, 'c-c');
47301         }
47302     },
47303
47304     initEvents : function(){
47305         Roo.form.Checkbox.superclass.initEvents.call(this);
47306         this.el.on("click", this.onClick,  this);
47307         this.el.on("change", this.onClick,  this);
47308     },
47309
47310
47311     getResizeEl : function(){
47312         return this.wrap;
47313     },
47314
47315     getPositionEl : function(){
47316         return this.wrap;
47317     },
47318
47319     
47320     // private
47321     onRender : function(ct, position){
47322         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47323        
47324         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47325         
47326         var r1 = '<table><tr>';
47327         var r2 = '<tr class="x-form-daypick-icons">';
47328         for (var i=0; i < 7; i++) {
47329             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47330             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47331         }
47332         
47333         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47334         viewEl.select('img').on('click', this.onClick, this);
47335         this.viewEl = viewEl;   
47336         
47337         
47338         // this will not work on Chrome!!!
47339         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47340         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47341         
47342         
47343           
47344
47345     },
47346
47347     // private
47348     initValue : Roo.emptyFn,
47349
47350     /**
47351      * Returns the checked state of the checkbox.
47352      * @return {Boolean} True if checked, else false
47353      */
47354     getValue : function(){
47355         return this.el.dom.value;
47356         
47357     },
47358
47359         // private
47360     onClick : function(e){ 
47361         //this.setChecked(!this.checked);
47362         Roo.get(e.target).toggleClass('x-menu-item-checked');
47363         this.refreshValue();
47364         //if(this.el.dom.checked != this.checked){
47365         //    this.setValue(this.el.dom.checked);
47366        // }
47367     },
47368     
47369     // private
47370     refreshValue : function()
47371     {
47372         var val = '';
47373         this.viewEl.select('img',true).each(function(e,i,n)  {
47374             val += e.is(".x-menu-item-checked") ? String(n) : '';
47375         });
47376         this.setValue(val, true);
47377     },
47378
47379     /**
47380      * Sets the checked state of the checkbox.
47381      * On is always based on a string comparison between inputValue and the param.
47382      * @param {Boolean/String} value - the value to set 
47383      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47384      */
47385     setValue : function(v,suppressEvent){
47386         if (!this.el.dom) {
47387             return;
47388         }
47389         var old = this.el.dom.value ;
47390         this.el.dom.value = v;
47391         if (suppressEvent) {
47392             return ;
47393         }
47394          
47395         // update display..
47396         this.viewEl.select('img',true).each(function(e,i,n)  {
47397             
47398             var on = e.is(".x-menu-item-checked");
47399             var newv = v.indexOf(String(n)) > -1;
47400             if (on != newv) {
47401                 e.toggleClass('x-menu-item-checked');
47402             }
47403             
47404         });
47405         
47406         
47407         this.fireEvent('change', this, v, old);
47408         
47409         
47410     },
47411    
47412     // handle setting of hidden value by some other method!!?!?
47413     setFromHidden: function()
47414     {
47415         if(!this.el){
47416             return;
47417         }
47418         //console.log("SET FROM HIDDEN");
47419         //alert('setFrom hidden');
47420         this.setValue(this.el.dom.value);
47421     },
47422     
47423     onDestroy : function()
47424     {
47425         if(this.viewEl){
47426             Roo.get(this.viewEl).remove();
47427         }
47428          
47429         Roo.form.DayPicker.superclass.onDestroy.call(this);
47430     }
47431
47432 });/*
47433  * RooJS Library 1.1.1
47434  * Copyright(c) 2008-2011  Alan Knowles
47435  *
47436  * License - LGPL
47437  */
47438  
47439
47440 /**
47441  * @class Roo.form.ComboCheck
47442  * @extends Roo.form.ComboBox
47443  * A combobox for multiple select items.
47444  *
47445  * FIXME - could do with a reset button..
47446  * 
47447  * @constructor
47448  * Create a new ComboCheck
47449  * @param {Object} config Configuration options
47450  */
47451 Roo.form.ComboCheck = function(config){
47452     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47453     // should verify some data...
47454     // like
47455     // hiddenName = required..
47456     // displayField = required
47457     // valudField == required
47458     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47459     var _t = this;
47460     Roo.each(req, function(e) {
47461         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47462             throw "Roo.form.ComboCheck : missing value for: " + e;
47463         }
47464     });
47465     
47466     
47467 };
47468
47469 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47470      
47471      
47472     editable : false,
47473      
47474     selectedClass: 'x-menu-item-checked', 
47475     
47476     // private
47477     onRender : function(ct, position){
47478         var _t = this;
47479         
47480         
47481         
47482         if(!this.tpl){
47483             var cls = 'x-combo-list';
47484
47485             
47486             this.tpl =  new Roo.Template({
47487                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47488                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47489                    '<span>{' + this.displayField + '}</span>' +
47490                     '</div>' 
47491                 
47492             });
47493         }
47494  
47495         
47496         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47497         this.view.singleSelect = false;
47498         this.view.multiSelect = true;
47499         this.view.toggleSelect = true;
47500         this.pageTb.add(new Roo.Toolbar.Fill(), {
47501             
47502             text: 'Done',
47503             handler: function()
47504             {
47505                 _t.collapse();
47506             }
47507         });
47508     },
47509     
47510     onViewOver : function(e, t){
47511         // do nothing...
47512         return;
47513         
47514     },
47515     
47516     onViewClick : function(doFocus,index){
47517         return;
47518         
47519     },
47520     select: function () {
47521         //Roo.log("SELECT CALLED");
47522     },
47523      
47524     selectByValue : function(xv, scrollIntoView){
47525         var ar = this.getValueArray();
47526         var sels = [];
47527         
47528         Roo.each(ar, function(v) {
47529             if(v === undefined || v === null){
47530                 return;
47531             }
47532             var r = this.findRecord(this.valueField, v);
47533             if(r){
47534                 sels.push(this.store.indexOf(r))
47535                 
47536             }
47537         },this);
47538         this.view.select(sels);
47539         return false;
47540     },
47541     
47542     
47543     
47544     onSelect : function(record, index){
47545        // Roo.log("onselect Called");
47546        // this is only called by the clear button now..
47547         this.view.clearSelections();
47548         this.setValue('[]');
47549         if (this.value != this.valueBefore) {
47550             this.fireEvent('change', this, this.value, this.valueBefore);
47551             this.valueBefore = this.value;
47552         }
47553     },
47554     getValueArray : function()
47555     {
47556         var ar = [] ;
47557         
47558         try {
47559             //Roo.log(this.value);
47560             if (typeof(this.value) == 'undefined') {
47561                 return [];
47562             }
47563             var ar = Roo.decode(this.value);
47564             return  ar instanceof Array ? ar : []; //?? valid?
47565             
47566         } catch(e) {
47567             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47568             return [];
47569         }
47570          
47571     },
47572     expand : function ()
47573     {
47574         
47575         Roo.form.ComboCheck.superclass.expand.call(this);
47576         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47577         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47578         
47579
47580     },
47581     
47582     collapse : function(){
47583         Roo.form.ComboCheck.superclass.collapse.call(this);
47584         var sl = this.view.getSelectedIndexes();
47585         var st = this.store;
47586         var nv = [];
47587         var tv = [];
47588         var r;
47589         Roo.each(sl, function(i) {
47590             r = st.getAt(i);
47591             nv.push(r.get(this.valueField));
47592         },this);
47593         this.setValue(Roo.encode(nv));
47594         if (this.value != this.valueBefore) {
47595
47596             this.fireEvent('change', this, this.value, this.valueBefore);
47597             this.valueBefore = this.value;
47598         }
47599         
47600     },
47601     
47602     setValue : function(v){
47603         // Roo.log(v);
47604         this.value = v;
47605         
47606         var vals = this.getValueArray();
47607         var tv = [];
47608         Roo.each(vals, function(k) {
47609             var r = this.findRecord(this.valueField, k);
47610             if(r){
47611                 tv.push(r.data[this.displayField]);
47612             }else if(this.valueNotFoundText !== undefined){
47613                 tv.push( this.valueNotFoundText );
47614             }
47615         },this);
47616        // Roo.log(tv);
47617         
47618         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47619         this.hiddenField.value = v;
47620         this.value = v;
47621     }
47622     
47623 });/*
47624  * Based on:
47625  * Ext JS Library 1.1.1
47626  * Copyright(c) 2006-2007, Ext JS, LLC.
47627  *
47628  * Originally Released Under LGPL - original licence link has changed is not relivant.
47629  *
47630  * Fork - LGPL
47631  * <script type="text/javascript">
47632  */
47633  
47634 /**
47635  * @class Roo.form.Signature
47636  * @extends Roo.form.Field
47637  * Signature field.  
47638  * @constructor
47639  * 
47640  * @param {Object} config Configuration options
47641  */
47642
47643 Roo.form.Signature = function(config){
47644     Roo.form.Signature.superclass.constructor.call(this, config);
47645     
47646     this.addEvents({// not in used??
47647          /**
47648          * @event confirm
47649          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47650              * @param {Roo.form.Signature} combo This combo box
47651              */
47652         'confirm' : true,
47653         /**
47654          * @event reset
47655          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47656              * @param {Roo.form.ComboBox} combo This combo box
47657              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47658              */
47659         'reset' : true
47660     });
47661 };
47662
47663 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47664     /**
47665      * @cfg {Object} labels Label to use when rendering a form.
47666      * defaults to 
47667      * labels : { 
47668      *      clear : "Clear",
47669      *      confirm : "Confirm"
47670      *  }
47671      */
47672     labels : { 
47673         clear : "Clear",
47674         confirm : "Confirm"
47675     },
47676     /**
47677      * @cfg {Number} width The signature panel width (defaults to 300)
47678      */
47679     width: 300,
47680     /**
47681      * @cfg {Number} height The signature panel height (defaults to 100)
47682      */
47683     height : 100,
47684     /**
47685      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47686      */
47687     allowBlank : false,
47688     
47689     //private
47690     // {Object} signPanel The signature SVG panel element (defaults to {})
47691     signPanel : {},
47692     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47693     isMouseDown : false,
47694     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47695     isConfirmed : false,
47696     // {String} signatureTmp SVG mapping string (defaults to empty string)
47697     signatureTmp : '',
47698     
47699     
47700     defaultAutoCreate : { // modified by initCompnoent..
47701         tag: "input",
47702         type:"hidden"
47703     },
47704
47705     // private
47706     onRender : function(ct, position){
47707         
47708         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47709         
47710         this.wrap = this.el.wrap({
47711             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47712         });
47713         
47714         this.createToolbar(this);
47715         this.signPanel = this.wrap.createChild({
47716                 tag: 'div',
47717                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47718             }, this.el
47719         );
47720             
47721         this.svgID = Roo.id();
47722         this.svgEl = this.signPanel.createChild({
47723               xmlns : 'http://www.w3.org/2000/svg',
47724               tag : 'svg',
47725               id : this.svgID + "-svg",
47726               width: this.width,
47727               height: this.height,
47728               viewBox: '0 0 '+this.width+' '+this.height,
47729               cn : [
47730                 {
47731                     tag: "rect",
47732                     id: this.svgID + "-svg-r",
47733                     width: this.width,
47734                     height: this.height,
47735                     fill: "#ffa"
47736                 },
47737                 {
47738                     tag: "line",
47739                     id: this.svgID + "-svg-l",
47740                     x1: "0", // start
47741                     y1: (this.height*0.8), // start set the line in 80% of height
47742                     x2: this.width, // end
47743                     y2: (this.height*0.8), // end set the line in 80% of height
47744                     'stroke': "#666",
47745                     'stroke-width': "1",
47746                     'stroke-dasharray': "3",
47747                     'shape-rendering': "crispEdges",
47748                     'pointer-events': "none"
47749                 },
47750                 {
47751                     tag: "path",
47752                     id: this.svgID + "-svg-p",
47753                     'stroke': "navy",
47754                     'stroke-width': "3",
47755                     'fill': "none",
47756                     'pointer-events': 'none'
47757                 }
47758               ]
47759         });
47760         this.createSVG();
47761         this.svgBox = this.svgEl.dom.getScreenCTM();
47762     },
47763     createSVG : function(){ 
47764         var svg = this.signPanel;
47765         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47766         var t = this;
47767
47768         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47769         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47770         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47771         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47772         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47773         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47774         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47775         
47776     },
47777     isTouchEvent : function(e){
47778         return e.type.match(/^touch/);
47779     },
47780     getCoords : function (e) {
47781         var pt    = this.svgEl.dom.createSVGPoint();
47782         pt.x = e.clientX; 
47783         pt.y = e.clientY;
47784         if (this.isTouchEvent(e)) {
47785             pt.x =  e.targetTouches[0].clientX 
47786             pt.y = e.targetTouches[0].clientY;
47787         }
47788         var a = this.svgEl.dom.getScreenCTM();
47789         var b = a.inverse();
47790         var mx = pt.matrixTransform(b);
47791         return mx.x + ',' + mx.y;
47792     },
47793     //mouse event headler 
47794     down : function (e) {
47795         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47796         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47797         
47798         this.isMouseDown = true;
47799         
47800         e.preventDefault();
47801     },
47802     move : function (e) {
47803         if (this.isMouseDown) {
47804             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47805             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47806         }
47807         
47808         e.preventDefault();
47809     },
47810     up : function (e) {
47811         this.isMouseDown = false;
47812         var sp = this.signatureTmp.split(' ');
47813         
47814         if(sp.length > 1){
47815             if(!sp[sp.length-2].match(/^L/)){
47816                 sp.pop();
47817                 sp.pop();
47818                 sp.push("");
47819                 this.signatureTmp = sp.join(" ");
47820             }
47821         }
47822         if(this.getValue() != this.signatureTmp){
47823             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47824             this.isConfirmed = false;
47825         }
47826         e.preventDefault();
47827     },
47828     
47829     /**
47830      * Protected method that will not generally be called directly. It
47831      * is called when the editor creates its toolbar. Override this method if you need to
47832      * add custom toolbar buttons.
47833      * @param {HtmlEditor} editor
47834      */
47835     createToolbar : function(editor){
47836          function btn(id, toggle, handler){
47837             var xid = fid + '-'+ id ;
47838             return {
47839                 id : xid,
47840                 cmd : id,
47841                 cls : 'x-btn-icon x-edit-'+id,
47842                 enableToggle:toggle !== false,
47843                 scope: editor, // was editor...
47844                 handler:handler||editor.relayBtnCmd,
47845                 clickEvent:'mousedown',
47846                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47847                 tabIndex:-1
47848             };
47849         }
47850         
47851         
47852         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47853         this.tb = tb;
47854         this.tb.add(
47855            {
47856                 cls : ' x-signature-btn x-signature-'+id,
47857                 scope: editor, // was editor...
47858                 handler: this.reset,
47859                 clickEvent:'mousedown',
47860                 text: this.labels.clear
47861             },
47862             {
47863                  xtype : 'Fill',
47864                  xns: Roo.Toolbar
47865             }, 
47866             {
47867                 cls : '  x-signature-btn x-signature-'+id,
47868                 scope: editor, // was editor...
47869                 handler: this.confirmHandler,
47870                 clickEvent:'mousedown',
47871                 text: this.labels.confirm
47872             }
47873         );
47874     
47875     },
47876     //public
47877     /**
47878      * when user is clicked confirm then show this image.....
47879      * 
47880      * @return {String} Image Data URI
47881      */
47882     getImageDataURI : function(){
47883         var svg = this.svgEl.dom.parentNode.innerHTML;
47884         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47885         return src; 
47886     },
47887     /**
47888      * 
47889      * @return {Boolean} this.isConfirmed
47890      */
47891     getConfirmed : function(){
47892         return this.isConfirmed;
47893     },
47894     /**
47895      * 
47896      * @return {Number} this.width
47897      */
47898     getWidth : function(){
47899         return this.width;
47900     },
47901     /**
47902      * 
47903      * @return {Number} this.height
47904      */
47905     getHeight : function(){
47906         return this.height;
47907     },
47908     // private
47909     getSignature : function(){
47910         return this.signatureTmp;
47911     },
47912     // private
47913     reset : function(){
47914         this.signatureTmp = '';
47915         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47916         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47917         this.isConfirmed = false;
47918         Roo.form.Signature.superclass.reset.call(this);
47919     },
47920     setSignature : function(s){
47921         this.signatureTmp = s;
47922         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47923         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47924         this.setValue(s);
47925         this.isConfirmed = false;
47926         Roo.form.Signature.superclass.reset.call(this);
47927     }, 
47928     test : function(){
47929 //        Roo.log(this.signPanel.dom.contentWindow.up())
47930     },
47931     //private
47932     setConfirmed : function(){
47933         
47934         
47935         
47936 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47937     },
47938     // private
47939     confirmHandler : function(){
47940         if(!this.getSignature()){
47941             return;
47942         }
47943         
47944         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47945         this.setValue(this.getSignature());
47946         this.isConfirmed = true;
47947         
47948         this.fireEvent('confirm', this);
47949     },
47950     // private
47951     // Subclasses should provide the validation implementation by overriding this
47952     validateValue : function(value){
47953         if(this.allowBlank){
47954             return true;
47955         }
47956         
47957         if(this.isConfirmed){
47958             return true;
47959         }
47960         return false;
47961     }
47962 });/*
47963  * Based on:
47964  * Ext JS Library 1.1.1
47965  * Copyright(c) 2006-2007, Ext JS, LLC.
47966  *
47967  * Originally Released Under LGPL - original licence link has changed is not relivant.
47968  *
47969  * Fork - LGPL
47970  * <script type="text/javascript">
47971  */
47972  
47973
47974 /**
47975  * @class Roo.form.ComboBox
47976  * @extends Roo.form.TriggerField
47977  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47978  * @constructor
47979  * Create a new ComboBox.
47980  * @param {Object} config Configuration options
47981  */
47982 Roo.form.Select = function(config){
47983     Roo.form.Select.superclass.constructor.call(this, config);
47984      
47985 };
47986
47987 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47988     /**
47989      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47990      */
47991     /**
47992      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47993      * rendering into an Roo.Editor, defaults to false)
47994      */
47995     /**
47996      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47997      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47998      */
47999     /**
48000      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48001      */
48002     /**
48003      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48004      * the dropdown list (defaults to undefined, with no header element)
48005      */
48006
48007      /**
48008      * @cfg {String/Roo.Template} tpl The template to use to render the output
48009      */
48010      
48011     // private
48012     defaultAutoCreate : {tag: "select"  },
48013     /**
48014      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48015      */
48016     listWidth: undefined,
48017     /**
48018      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48019      * mode = 'remote' or 'text' if mode = 'local')
48020      */
48021     displayField: undefined,
48022     /**
48023      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48024      * mode = 'remote' or 'value' if mode = 'local'). 
48025      * Note: use of a valueField requires the user make a selection
48026      * in order for a value to be mapped.
48027      */
48028     valueField: undefined,
48029     
48030     
48031     /**
48032      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48033      * field's data value (defaults to the underlying DOM element's name)
48034      */
48035     hiddenName: undefined,
48036     /**
48037      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48038      */
48039     listClass: '',
48040     /**
48041      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48042      */
48043     selectedClass: 'x-combo-selected',
48044     /**
48045      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48046      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48047      * which displays a downward arrow icon).
48048      */
48049     triggerClass : 'x-form-arrow-trigger',
48050     /**
48051      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48052      */
48053     shadow:'sides',
48054     /**
48055      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48056      * anchor positions (defaults to 'tl-bl')
48057      */
48058     listAlign: 'tl-bl?',
48059     /**
48060      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48061      */
48062     maxHeight: 300,
48063     /**
48064      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48065      * query specified by the allQuery config option (defaults to 'query')
48066      */
48067     triggerAction: 'query',
48068     /**
48069      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48070      * (defaults to 4, does not apply if editable = false)
48071      */
48072     minChars : 4,
48073     /**
48074      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48075      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48076      */
48077     typeAhead: false,
48078     /**
48079      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48080      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48081      */
48082     queryDelay: 500,
48083     /**
48084      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48085      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48086      */
48087     pageSize: 0,
48088     /**
48089      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48090      * when editable = true (defaults to false)
48091      */
48092     selectOnFocus:false,
48093     /**
48094      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48095      */
48096     queryParam: 'query',
48097     /**
48098      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48099      * when mode = 'remote' (defaults to 'Loading...')
48100      */
48101     loadingText: 'Loading...',
48102     /**
48103      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48104      */
48105     resizable: false,
48106     /**
48107      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48108      */
48109     handleHeight : 8,
48110     /**
48111      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48112      * traditional select (defaults to true)
48113      */
48114     editable: true,
48115     /**
48116      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48117      */
48118     allQuery: '',
48119     /**
48120      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48121      */
48122     mode: 'remote',
48123     /**
48124      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48125      * listWidth has a higher value)
48126      */
48127     minListWidth : 70,
48128     /**
48129      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48130      * allow the user to set arbitrary text into the field (defaults to false)
48131      */
48132     forceSelection:false,
48133     /**
48134      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48135      * if typeAhead = true (defaults to 250)
48136      */
48137     typeAheadDelay : 250,
48138     /**
48139      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48140      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48141      */
48142     valueNotFoundText : undefined,
48143     
48144     /**
48145      * @cfg {String} defaultValue The value displayed after loading the store.
48146      */
48147     defaultValue: '',
48148     
48149     /**
48150      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48151      */
48152     blockFocus : false,
48153     
48154     /**
48155      * @cfg {Boolean} disableClear Disable showing of clear button.
48156      */
48157     disableClear : false,
48158     /**
48159      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48160      */
48161     alwaysQuery : false,
48162     
48163     //private
48164     addicon : false,
48165     editicon: false,
48166     
48167     // element that contains real text value.. (when hidden is used..)
48168      
48169     // private
48170     onRender : function(ct, position){
48171         Roo.form.Field.prototype.onRender.call(this, ct, position);
48172         
48173         if(this.store){
48174             this.store.on('beforeload', this.onBeforeLoad, this);
48175             this.store.on('load', this.onLoad, this);
48176             this.store.on('loadexception', this.onLoadException, this);
48177             this.store.load({});
48178         }
48179         
48180         
48181         
48182     },
48183
48184     // private
48185     initEvents : function(){
48186         //Roo.form.ComboBox.superclass.initEvents.call(this);
48187  
48188     },
48189
48190     onDestroy : function(){
48191        
48192         if(this.store){
48193             this.store.un('beforeload', this.onBeforeLoad, this);
48194             this.store.un('load', this.onLoad, this);
48195             this.store.un('loadexception', this.onLoadException, this);
48196         }
48197         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48198     },
48199
48200     // private
48201     fireKey : function(e){
48202         if(e.isNavKeyPress() && !this.list.isVisible()){
48203             this.fireEvent("specialkey", this, e);
48204         }
48205     },
48206
48207     // private
48208     onResize: function(w, h){
48209         
48210         return; 
48211     
48212         
48213     },
48214
48215     /**
48216      * Allow or prevent the user from directly editing the field text.  If false is passed,
48217      * the user will only be able to select from the items defined in the dropdown list.  This method
48218      * is the runtime equivalent of setting the 'editable' config option at config time.
48219      * @param {Boolean} value True to allow the user to directly edit the field text
48220      */
48221     setEditable : function(value){
48222          
48223     },
48224
48225     // private
48226     onBeforeLoad : function(){
48227         
48228         Roo.log("Select before load");
48229         return;
48230     
48231         this.innerList.update(this.loadingText ?
48232                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48233         //this.restrictHeight();
48234         this.selectedIndex = -1;
48235     },
48236
48237     // private
48238     onLoad : function(){
48239
48240     
48241         var dom = this.el.dom;
48242         dom.innerHTML = '';
48243          var od = dom.ownerDocument;
48244          
48245         if (this.emptyText) {
48246             var op = od.createElement('option');
48247             op.setAttribute('value', '');
48248             op.innerHTML = String.format('{0}', this.emptyText);
48249             dom.appendChild(op);
48250         }
48251         if(this.store.getCount() > 0){
48252            
48253             var vf = this.valueField;
48254             var df = this.displayField;
48255             this.store.data.each(function(r) {
48256                 // which colmsn to use... testing - cdoe / title..
48257                 var op = od.createElement('option');
48258                 op.setAttribute('value', r.data[vf]);
48259                 op.innerHTML = String.format('{0}', r.data[df]);
48260                 dom.appendChild(op);
48261             });
48262             if (typeof(this.defaultValue != 'undefined')) {
48263                 this.setValue(this.defaultValue);
48264             }
48265             
48266              
48267         }else{
48268             //this.onEmptyResults();
48269         }
48270         //this.el.focus();
48271     },
48272     // private
48273     onLoadException : function()
48274     {
48275         dom.innerHTML = '';
48276             
48277         Roo.log("Select on load exception");
48278         return;
48279     
48280         this.collapse();
48281         Roo.log(this.store.reader.jsonData);
48282         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48283             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48284         }
48285         
48286         
48287     },
48288     // private
48289     onTypeAhead : function(){
48290          
48291     },
48292
48293     // private
48294     onSelect : function(record, index){
48295         Roo.log('on select?');
48296         return;
48297         if(this.fireEvent('beforeselect', this, record, index) !== false){
48298             this.setFromData(index > -1 ? record.data : false);
48299             this.collapse();
48300             this.fireEvent('select', this, record, index);
48301         }
48302     },
48303
48304     /**
48305      * Returns the currently selected field value or empty string if no value is set.
48306      * @return {String} value The selected value
48307      */
48308     getValue : function(){
48309         var dom = this.el.dom;
48310         this.value = dom.options[dom.selectedIndex].value;
48311         return this.value;
48312         
48313     },
48314
48315     /**
48316      * Clears any text/value currently set in the field
48317      */
48318     clearValue : function(){
48319         this.value = '';
48320         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48321         
48322     },
48323
48324     /**
48325      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48326      * will be displayed in the field.  If the value does not match the data value of an existing item,
48327      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48328      * Otherwise the field will be blank (although the value will still be set).
48329      * @param {String} value The value to match
48330      */
48331     setValue : function(v){
48332         var d = this.el.dom;
48333         for (var i =0; i < d.options.length;i++) {
48334             if (v == d.options[i].value) {
48335                 d.selectedIndex = i;
48336                 this.value = v;
48337                 return;
48338             }
48339         }
48340         this.clearValue();
48341     },
48342     /**
48343      * @property {Object} the last set data for the element
48344      */
48345     
48346     lastData : false,
48347     /**
48348      * Sets the value of the field based on a object which is related to the record format for the store.
48349      * @param {Object} value the value to set as. or false on reset?
48350      */
48351     setFromData : function(o){
48352         Roo.log('setfrom data?');
48353          
48354         
48355         
48356     },
48357     // private
48358     reset : function(){
48359         this.clearValue();
48360     },
48361     // private
48362     findRecord : function(prop, value){
48363         
48364         return false;
48365     
48366         var record;
48367         if(this.store.getCount() > 0){
48368             this.store.each(function(r){
48369                 if(r.data[prop] == value){
48370                     record = r;
48371                     return false;
48372                 }
48373                 return true;
48374             });
48375         }
48376         return record;
48377     },
48378     
48379     getName: function()
48380     {
48381         // returns hidden if it's set..
48382         if (!this.rendered) {return ''};
48383         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48384         
48385     },
48386      
48387
48388     
48389
48390     // private
48391     onEmptyResults : function(){
48392         Roo.log('empty results');
48393         //this.collapse();
48394     },
48395
48396     /**
48397      * Returns true if the dropdown list is expanded, else false.
48398      */
48399     isExpanded : function(){
48400         return false;
48401     },
48402
48403     /**
48404      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48405      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48406      * @param {String} value The data value of the item to select
48407      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48408      * selected item if it is not currently in view (defaults to true)
48409      * @return {Boolean} True if the value matched an item in the list, else false
48410      */
48411     selectByValue : function(v, scrollIntoView){
48412         Roo.log('select By Value');
48413         return false;
48414     
48415         if(v !== undefined && v !== null){
48416             var r = this.findRecord(this.valueField || this.displayField, v);
48417             if(r){
48418                 this.select(this.store.indexOf(r), scrollIntoView);
48419                 return true;
48420             }
48421         }
48422         return false;
48423     },
48424
48425     /**
48426      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48427      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48428      * @param {Number} index The zero-based index of the list item to select
48429      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48430      * selected item if it is not currently in view (defaults to true)
48431      */
48432     select : function(index, scrollIntoView){
48433         Roo.log('select ');
48434         return  ;
48435         
48436         this.selectedIndex = index;
48437         this.view.select(index);
48438         if(scrollIntoView !== false){
48439             var el = this.view.getNode(index);
48440             if(el){
48441                 this.innerList.scrollChildIntoView(el, false);
48442             }
48443         }
48444     },
48445
48446       
48447
48448     // private
48449     validateBlur : function(){
48450         
48451         return;
48452         
48453     },
48454
48455     // private
48456     initQuery : function(){
48457         this.doQuery(this.getRawValue());
48458     },
48459
48460     // private
48461     doForce : function(){
48462         if(this.el.dom.value.length > 0){
48463             this.el.dom.value =
48464                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48465              
48466         }
48467     },
48468
48469     /**
48470      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48471      * query allowing the query action to be canceled if needed.
48472      * @param {String} query The SQL query to execute
48473      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48474      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48475      * saved in the current store (defaults to false)
48476      */
48477     doQuery : function(q, forceAll){
48478         
48479         Roo.log('doQuery?');
48480         if(q === undefined || q === null){
48481             q = '';
48482         }
48483         var qe = {
48484             query: q,
48485             forceAll: forceAll,
48486             combo: this,
48487             cancel:false
48488         };
48489         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48490             return false;
48491         }
48492         q = qe.query;
48493         forceAll = qe.forceAll;
48494         if(forceAll === true || (q.length >= this.minChars)){
48495             if(this.lastQuery != q || this.alwaysQuery){
48496                 this.lastQuery = q;
48497                 if(this.mode == 'local'){
48498                     this.selectedIndex = -1;
48499                     if(forceAll){
48500                         this.store.clearFilter();
48501                     }else{
48502                         this.store.filter(this.displayField, q);
48503                     }
48504                     this.onLoad();
48505                 }else{
48506                     this.store.baseParams[this.queryParam] = q;
48507                     this.store.load({
48508                         params: this.getParams(q)
48509                     });
48510                     this.expand();
48511                 }
48512             }else{
48513                 this.selectedIndex = -1;
48514                 this.onLoad();   
48515             }
48516         }
48517     },
48518
48519     // private
48520     getParams : function(q){
48521         var p = {};
48522         //p[this.queryParam] = q;
48523         if(this.pageSize){
48524             p.start = 0;
48525             p.limit = this.pageSize;
48526         }
48527         return p;
48528     },
48529
48530     /**
48531      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48532      */
48533     collapse : function(){
48534         
48535     },
48536
48537     // private
48538     collapseIf : function(e){
48539         
48540     },
48541
48542     /**
48543      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48544      */
48545     expand : function(){
48546         
48547     } ,
48548
48549     // private
48550      
48551
48552     /** 
48553     * @cfg {Boolean} grow 
48554     * @hide 
48555     */
48556     /** 
48557     * @cfg {Number} growMin 
48558     * @hide 
48559     */
48560     /** 
48561     * @cfg {Number} growMax 
48562     * @hide 
48563     */
48564     /**
48565      * @hide
48566      * @method autoSize
48567      */
48568     
48569     setWidth : function()
48570     {
48571         
48572     },
48573     getResizeEl : function(){
48574         return this.el;
48575     }
48576 });//<script type="text/javasscript">
48577  
48578
48579 /**
48580  * @class Roo.DDView
48581  * A DnD enabled version of Roo.View.
48582  * @param {Element/String} container The Element in which to create the View.
48583  * @param {String} tpl The template string used to create the markup for each element of the View
48584  * @param {Object} config The configuration properties. These include all the config options of
48585  * {@link Roo.View} plus some specific to this class.<br>
48586  * <p>
48587  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48588  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48589  * <p>
48590  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48591 .x-view-drag-insert-above {
48592         border-top:1px dotted #3366cc;
48593 }
48594 .x-view-drag-insert-below {
48595         border-bottom:1px dotted #3366cc;
48596 }
48597 </code></pre>
48598  * 
48599  */
48600  
48601 Roo.DDView = function(container, tpl, config) {
48602     Roo.DDView.superclass.constructor.apply(this, arguments);
48603     this.getEl().setStyle("outline", "0px none");
48604     this.getEl().unselectable();
48605     if (this.dragGroup) {
48606                 this.setDraggable(this.dragGroup.split(","));
48607     }
48608     if (this.dropGroup) {
48609                 this.setDroppable(this.dropGroup.split(","));
48610     }
48611     if (this.deletable) {
48612         this.setDeletable();
48613     }
48614     this.isDirtyFlag = false;
48615         this.addEvents({
48616                 "drop" : true
48617         });
48618 };
48619
48620 Roo.extend(Roo.DDView, Roo.View, {
48621 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48622 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48623 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48624 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48625
48626         isFormField: true,
48627
48628         reset: Roo.emptyFn,
48629         
48630         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48631
48632         validate: function() {
48633                 return true;
48634         },
48635         
48636         destroy: function() {
48637                 this.purgeListeners();
48638                 this.getEl.removeAllListeners();
48639                 this.getEl().remove();
48640                 if (this.dragZone) {
48641                         if (this.dragZone.destroy) {
48642                                 this.dragZone.destroy();
48643                         }
48644                 }
48645                 if (this.dropZone) {
48646                         if (this.dropZone.destroy) {
48647                                 this.dropZone.destroy();
48648                         }
48649                 }
48650         },
48651
48652 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48653         getName: function() {
48654                 return this.name;
48655         },
48656
48657 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48658         setValue: function(v) {
48659                 if (!this.store) {
48660                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48661                 }
48662                 var data = {};
48663                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48664                 this.store.proxy = new Roo.data.MemoryProxy(data);
48665                 this.store.load();
48666         },
48667
48668 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48669         getValue: function() {
48670                 var result = '(';
48671                 this.store.each(function(rec) {
48672                         result += rec.id + ',';
48673                 });
48674                 return result.substr(0, result.length - 1) + ')';
48675         },
48676         
48677         getIds: function() {
48678                 var i = 0, result = new Array(this.store.getCount());
48679                 this.store.each(function(rec) {
48680                         result[i++] = rec.id;
48681                 });
48682                 return result;
48683         },
48684         
48685         isDirty: function() {
48686                 return this.isDirtyFlag;
48687         },
48688
48689 /**
48690  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48691  *      whole Element becomes the target, and this causes the drop gesture to append.
48692  */
48693     getTargetFromEvent : function(e) {
48694                 var target = e.getTarget();
48695                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48696                 target = target.parentNode;
48697                 }
48698                 if (!target) {
48699                         target = this.el.dom.lastChild || this.el.dom;
48700                 }
48701                 return target;
48702     },
48703
48704 /**
48705  *      Create the drag data which consists of an object which has the property "ddel" as
48706  *      the drag proxy element. 
48707  */
48708     getDragData : function(e) {
48709         var target = this.findItemFromChild(e.getTarget());
48710                 if(target) {
48711                         this.handleSelection(e);
48712                         var selNodes = this.getSelectedNodes();
48713             var dragData = {
48714                 source: this,
48715                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48716                 nodes: selNodes,
48717                 records: []
48718                         };
48719                         var selectedIndices = this.getSelectedIndexes();
48720                         for (var i = 0; i < selectedIndices.length; i++) {
48721                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48722                         }
48723                         if (selNodes.length == 1) {
48724                                 dragData.ddel = target.cloneNode(true); // the div element
48725                         } else {
48726                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48727                                 div.className = 'multi-proxy';
48728                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48729                                         div.appendChild(selNodes[i].cloneNode(true));
48730                                 }
48731                                 dragData.ddel = div;
48732                         }
48733             //console.log(dragData)
48734             //console.log(dragData.ddel.innerHTML)
48735                         return dragData;
48736                 }
48737         //console.log('nodragData')
48738                 return false;
48739     },
48740     
48741 /**     Specify to which ddGroup items in this DDView may be dragged. */
48742     setDraggable: function(ddGroup) {
48743         if (ddGroup instanceof Array) {
48744                 Roo.each(ddGroup, this.setDraggable, this);
48745                 return;
48746         }
48747         if (this.dragZone) {
48748                 this.dragZone.addToGroup(ddGroup);
48749         } else {
48750                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48751                                 containerScroll: true,
48752                                 ddGroup: ddGroup 
48753
48754                         });
48755 //                      Draggability implies selection. DragZone's mousedown selects the element.
48756                         if (!this.multiSelect) { this.singleSelect = true; }
48757
48758 //                      Wire the DragZone's handlers up to methods in *this*
48759                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48760                 }
48761     },
48762
48763 /**     Specify from which ddGroup this DDView accepts drops. */
48764     setDroppable: function(ddGroup) {
48765         if (ddGroup instanceof Array) {
48766                 Roo.each(ddGroup, this.setDroppable, this);
48767                 return;
48768         }
48769         if (this.dropZone) {
48770                 this.dropZone.addToGroup(ddGroup);
48771         } else {
48772                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48773                                 containerScroll: true,
48774                                 ddGroup: ddGroup
48775                         });
48776
48777 //                      Wire the DropZone's handlers up to methods in *this*
48778                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48779                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48780                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48781                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48782                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48783                 }
48784     },
48785
48786 /**     Decide whether to drop above or below a View node. */
48787     getDropPoint : function(e, n, dd){
48788         if (n == this.el.dom) { return "above"; }
48789                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48790                 var c = t + (b - t) / 2;
48791                 var y = Roo.lib.Event.getPageY(e);
48792                 if(y <= c) {
48793                         return "above";
48794                 }else{
48795                         return "below";
48796                 }
48797     },
48798
48799     onNodeEnter : function(n, dd, e, data){
48800                 return false;
48801     },
48802     
48803     onNodeOver : function(n, dd, e, data){
48804                 var pt = this.getDropPoint(e, n, dd);
48805                 // set the insert point style on the target node
48806                 var dragElClass = this.dropNotAllowed;
48807                 if (pt) {
48808                         var targetElClass;
48809                         if (pt == "above"){
48810                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48811                                 targetElClass = "x-view-drag-insert-above";
48812                         } else {
48813                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48814                                 targetElClass = "x-view-drag-insert-below";
48815                         }
48816                         if (this.lastInsertClass != targetElClass){
48817                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48818                                 this.lastInsertClass = targetElClass;
48819                         }
48820                 }
48821                 return dragElClass;
48822         },
48823
48824     onNodeOut : function(n, dd, e, data){
48825                 this.removeDropIndicators(n);
48826     },
48827
48828     onNodeDrop : function(n, dd, e, data){
48829         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48830                 return false;
48831         }
48832         var pt = this.getDropPoint(e, n, dd);
48833                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48834                 if (pt == "below") { insertAt++; }
48835                 for (var i = 0; i < data.records.length; i++) {
48836                         var r = data.records[i];
48837                         var dup = this.store.getById(r.id);
48838                         if (dup && (dd != this.dragZone)) {
48839                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48840                         } else {
48841                                 if (data.copy) {
48842                                         this.store.insert(insertAt++, r.copy());
48843                                 } else {
48844                                         data.source.isDirtyFlag = true;
48845                                         r.store.remove(r);
48846                                         this.store.insert(insertAt++, r);
48847                                 }
48848                                 this.isDirtyFlag = true;
48849                         }
48850                 }
48851                 this.dragZone.cachedTarget = null;
48852                 return true;
48853     },
48854
48855     removeDropIndicators : function(n){
48856                 if(n){
48857                         Roo.fly(n).removeClass([
48858                                 "x-view-drag-insert-above",
48859                                 "x-view-drag-insert-below"]);
48860                         this.lastInsertClass = "_noclass";
48861                 }
48862     },
48863
48864 /**
48865  *      Utility method. Add a delete option to the DDView's context menu.
48866  *      @param {String} imageUrl The URL of the "delete" icon image.
48867  */
48868         setDeletable: function(imageUrl) {
48869                 if (!this.singleSelect && !this.multiSelect) {
48870                         this.singleSelect = true;
48871                 }
48872                 var c = this.getContextMenu();
48873                 this.contextMenu.on("itemclick", function(item) {
48874                         switch (item.id) {
48875                                 case "delete":
48876                                         this.remove(this.getSelectedIndexes());
48877                                         break;
48878                         }
48879                 }, this);
48880                 this.contextMenu.add({
48881                         icon: imageUrl,
48882                         id: "delete",
48883                         text: 'Delete'
48884                 });
48885         },
48886         
48887 /**     Return the context menu for this DDView. */
48888         getContextMenu: function() {
48889                 if (!this.contextMenu) {
48890 //                      Create the View's context menu
48891                         this.contextMenu = new Roo.menu.Menu({
48892                                 id: this.id + "-contextmenu"
48893                         });
48894                         this.el.on("contextmenu", this.showContextMenu, this);
48895                 }
48896                 return this.contextMenu;
48897         },
48898         
48899         disableContextMenu: function() {
48900                 if (this.contextMenu) {
48901                         this.el.un("contextmenu", this.showContextMenu, this);
48902                 }
48903         },
48904
48905         showContextMenu: function(e, item) {
48906         item = this.findItemFromChild(e.getTarget());
48907                 if (item) {
48908                         e.stopEvent();
48909                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48910                         this.contextMenu.showAt(e.getXY());
48911             }
48912     },
48913
48914 /**
48915  *      Remove {@link Roo.data.Record}s at the specified indices.
48916  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48917  */
48918     remove: function(selectedIndices) {
48919                 selectedIndices = [].concat(selectedIndices);
48920                 for (var i = 0; i < selectedIndices.length; i++) {
48921                         var rec = this.store.getAt(selectedIndices[i]);
48922                         this.store.remove(rec);
48923                 }
48924     },
48925
48926 /**
48927  *      Double click fires the event, but also, if this is draggable, and there is only one other
48928  *      related DropZone, it transfers the selected node.
48929  */
48930     onDblClick : function(e){
48931         var item = this.findItemFromChild(e.getTarget());
48932         if(item){
48933             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48934                 return false;
48935             }
48936             if (this.dragGroup) {
48937                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48938                     while (targets.indexOf(this.dropZone) > -1) {
48939                             targets.remove(this.dropZone);
48940                                 }
48941                     if (targets.length == 1) {
48942                                         this.dragZone.cachedTarget = null;
48943                         var el = Roo.get(targets[0].getEl());
48944                         var box = el.getBox(true);
48945                         targets[0].onNodeDrop(el.dom, {
48946                                 target: el.dom,
48947                                 xy: [box.x, box.y + box.height - 1]
48948                         }, null, this.getDragData(e));
48949                     }
48950                 }
48951         }
48952     },
48953     
48954     handleSelection: function(e) {
48955                 this.dragZone.cachedTarget = null;
48956         var item = this.findItemFromChild(e.getTarget());
48957         if (!item) {
48958                 this.clearSelections(true);
48959                 return;
48960         }
48961                 if (item && (this.multiSelect || this.singleSelect)){
48962                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48963                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48964                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48965                                 this.unselect(item);
48966                         } else {
48967                                 this.select(item, this.multiSelect && e.ctrlKey);
48968                                 this.lastSelection = item;
48969                         }
48970                 }
48971     },
48972
48973     onItemClick : function(item, index, e){
48974                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48975                         return false;
48976                 }
48977                 return true;
48978     },
48979
48980     unselect : function(nodeInfo, suppressEvent){
48981                 var node = this.getNode(nodeInfo);
48982                 if(node && this.isSelected(node)){
48983                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48984                                 Roo.fly(node).removeClass(this.selectedClass);
48985                                 this.selections.remove(node);
48986                                 if(!suppressEvent){
48987                                         this.fireEvent("selectionchange", this, this.selections);
48988                                 }
48989                         }
48990                 }
48991     }
48992 });
48993 /*
48994  * Based on:
48995  * Ext JS Library 1.1.1
48996  * Copyright(c) 2006-2007, Ext JS, LLC.
48997  *
48998  * Originally Released Under LGPL - original licence link has changed is not relivant.
48999  *
49000  * Fork - LGPL
49001  * <script type="text/javascript">
49002  */
49003  
49004 /**
49005  * @class Roo.LayoutManager
49006  * @extends Roo.util.Observable
49007  * Base class for layout managers.
49008  */
49009 Roo.LayoutManager = function(container, config){
49010     Roo.LayoutManager.superclass.constructor.call(this);
49011     this.el = Roo.get(container);
49012     // ie scrollbar fix
49013     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49014         document.body.scroll = "no";
49015     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49016         this.el.position('relative');
49017     }
49018     this.id = this.el.id;
49019     this.el.addClass("x-layout-container");
49020     /** false to disable window resize monitoring @type Boolean */
49021     this.monitorWindowResize = true;
49022     this.regions = {};
49023     this.addEvents({
49024         /**
49025          * @event layout
49026          * Fires when a layout is performed. 
49027          * @param {Roo.LayoutManager} this
49028          */
49029         "layout" : true,
49030         /**
49031          * @event regionresized
49032          * Fires when the user resizes a region. 
49033          * @param {Roo.LayoutRegion} region The resized region
49034          * @param {Number} newSize The new size (width for east/west, height for north/south)
49035          */
49036         "regionresized" : true,
49037         /**
49038          * @event regioncollapsed
49039          * Fires when a region is collapsed. 
49040          * @param {Roo.LayoutRegion} region The collapsed region
49041          */
49042         "regioncollapsed" : true,
49043         /**
49044          * @event regionexpanded
49045          * Fires when a region is expanded.  
49046          * @param {Roo.LayoutRegion} region The expanded region
49047          */
49048         "regionexpanded" : true
49049     });
49050     this.updating = false;
49051     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49052 };
49053
49054 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49055     /**
49056      * Returns true if this layout is currently being updated
49057      * @return {Boolean}
49058      */
49059     isUpdating : function(){
49060         return this.updating; 
49061     },
49062     
49063     /**
49064      * Suspend the LayoutManager from doing auto-layouts while
49065      * making multiple add or remove calls
49066      */
49067     beginUpdate : function(){
49068         this.updating = true;    
49069     },
49070     
49071     /**
49072      * Restore auto-layouts and optionally disable the manager from performing a layout
49073      * @param {Boolean} noLayout true to disable a layout update 
49074      */
49075     endUpdate : function(noLayout){
49076         this.updating = false;
49077         if(!noLayout){
49078             this.layout();
49079         }    
49080     },
49081     
49082     layout: function(){
49083         
49084     },
49085     
49086     onRegionResized : function(region, newSize){
49087         this.fireEvent("regionresized", region, newSize);
49088         this.layout();
49089     },
49090     
49091     onRegionCollapsed : function(region){
49092         this.fireEvent("regioncollapsed", region);
49093     },
49094     
49095     onRegionExpanded : function(region){
49096         this.fireEvent("regionexpanded", region);
49097     },
49098         
49099     /**
49100      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49101      * performs box-model adjustments.
49102      * @return {Object} The size as an object {width: (the width), height: (the height)}
49103      */
49104     getViewSize : function(){
49105         var size;
49106         if(this.el.dom != document.body){
49107             size = this.el.getSize();
49108         }else{
49109             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49110         }
49111         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49112         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49113         return size;
49114     },
49115     
49116     /**
49117      * Returns the Element this layout is bound to.
49118      * @return {Roo.Element}
49119      */
49120     getEl : function(){
49121         return this.el;
49122     },
49123     
49124     /**
49125      * Returns the specified region.
49126      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49127      * @return {Roo.LayoutRegion}
49128      */
49129     getRegion : function(target){
49130         return this.regions[target.toLowerCase()];
49131     },
49132     
49133     onWindowResize : function(){
49134         if(this.monitorWindowResize){
49135             this.layout();
49136         }
49137     }
49138 });/*
49139  * Based on:
49140  * Ext JS Library 1.1.1
49141  * Copyright(c) 2006-2007, Ext JS, LLC.
49142  *
49143  * Originally Released Under LGPL - original licence link has changed is not relivant.
49144  *
49145  * Fork - LGPL
49146  * <script type="text/javascript">
49147  */
49148 /**
49149  * @class Roo.BorderLayout
49150  * @extends Roo.LayoutManager
49151  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49152  * please see: <br><br>
49153  * <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>
49154  * <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>
49155  * Example:
49156  <pre><code>
49157  var layout = new Roo.BorderLayout(document.body, {
49158     north: {
49159         initialSize: 25,
49160         titlebar: false
49161     },
49162     west: {
49163         split:true,
49164         initialSize: 200,
49165         minSize: 175,
49166         maxSize: 400,
49167         titlebar: true,
49168         collapsible: true
49169     },
49170     east: {
49171         split:true,
49172         initialSize: 202,
49173         minSize: 175,
49174         maxSize: 400,
49175         titlebar: true,
49176         collapsible: true
49177     },
49178     south: {
49179         split:true,
49180         initialSize: 100,
49181         minSize: 100,
49182         maxSize: 200,
49183         titlebar: true,
49184         collapsible: true
49185     },
49186     center: {
49187         titlebar: true,
49188         autoScroll:true,
49189         resizeTabs: true,
49190         minTabWidth: 50,
49191         preferredTabWidth: 150
49192     }
49193 });
49194
49195 // shorthand
49196 var CP = Roo.ContentPanel;
49197
49198 layout.beginUpdate();
49199 layout.add("north", new CP("north", "North"));
49200 layout.add("south", new CP("south", {title: "South", closable: true}));
49201 layout.add("west", new CP("west", {title: "West"}));
49202 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49203 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49204 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49205 layout.getRegion("center").showPanel("center1");
49206 layout.endUpdate();
49207 </code></pre>
49208
49209 <b>The container the layout is rendered into can be either the body element or any other element.
49210 If it is not the body element, the container needs to either be an absolute positioned element,
49211 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49212 the container size if it is not the body element.</b>
49213
49214 * @constructor
49215 * Create a new BorderLayout
49216 * @param {String/HTMLElement/Element} container The container this layout is bound to
49217 * @param {Object} config Configuration options
49218  */
49219 Roo.BorderLayout = function(container, config){
49220     config = config || {};
49221     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49222     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49223     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49224         var target = this.factory.validRegions[i];
49225         if(config[target]){
49226             this.addRegion(target, config[target]);
49227         }
49228     }
49229 };
49230
49231 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49232     /**
49233      * Creates and adds a new region if it doesn't already exist.
49234      * @param {String} target The target region key (north, south, east, west or center).
49235      * @param {Object} config The regions config object
49236      * @return {BorderLayoutRegion} The new region
49237      */
49238     addRegion : function(target, config){
49239         if(!this.regions[target]){
49240             var r = this.factory.create(target, this, config);
49241             this.bindRegion(target, r);
49242         }
49243         return this.regions[target];
49244     },
49245
49246     // private (kinda)
49247     bindRegion : function(name, r){
49248         this.regions[name] = r;
49249         r.on("visibilitychange", this.layout, this);
49250         r.on("paneladded", this.layout, this);
49251         r.on("panelremoved", this.layout, this);
49252         r.on("invalidated", this.layout, this);
49253         r.on("resized", this.onRegionResized, this);
49254         r.on("collapsed", this.onRegionCollapsed, this);
49255         r.on("expanded", this.onRegionExpanded, this);
49256     },
49257
49258     /**
49259      * Performs a layout update.
49260      */
49261     layout : function(){
49262         if(this.updating) return;
49263         var size = this.getViewSize();
49264         var w = size.width;
49265         var h = size.height;
49266         var centerW = w;
49267         var centerH = h;
49268         var centerY = 0;
49269         var centerX = 0;
49270         //var x = 0, y = 0;
49271
49272         var rs = this.regions;
49273         var north = rs["north"];
49274         var south = rs["south"]; 
49275         var west = rs["west"];
49276         var east = rs["east"];
49277         var center = rs["center"];
49278         //if(this.hideOnLayout){ // not supported anymore
49279             //c.el.setStyle("display", "none");
49280         //}
49281         if(north && north.isVisible()){
49282             var b = north.getBox();
49283             var m = north.getMargins();
49284             b.width = w - (m.left+m.right);
49285             b.x = m.left;
49286             b.y = m.top;
49287             centerY = b.height + b.y + m.bottom;
49288             centerH -= centerY;
49289             north.updateBox(this.safeBox(b));
49290         }
49291         if(south && south.isVisible()){
49292             var b = south.getBox();
49293             var m = south.getMargins();
49294             b.width = w - (m.left+m.right);
49295             b.x = m.left;
49296             var totalHeight = (b.height + m.top + m.bottom);
49297             b.y = h - totalHeight + m.top;
49298             centerH -= totalHeight;
49299             south.updateBox(this.safeBox(b));
49300         }
49301         if(west && west.isVisible()){
49302             var b = west.getBox();
49303             var m = west.getMargins();
49304             b.height = centerH - (m.top+m.bottom);
49305             b.x = m.left;
49306             b.y = centerY + m.top;
49307             var totalWidth = (b.width + m.left + m.right);
49308             centerX += totalWidth;
49309             centerW -= totalWidth;
49310             west.updateBox(this.safeBox(b));
49311         }
49312         if(east && east.isVisible()){
49313             var b = east.getBox();
49314             var m = east.getMargins();
49315             b.height = centerH - (m.top+m.bottom);
49316             var totalWidth = (b.width + m.left + m.right);
49317             b.x = w - totalWidth + m.left;
49318             b.y = centerY + m.top;
49319             centerW -= totalWidth;
49320             east.updateBox(this.safeBox(b));
49321         }
49322         if(center){
49323             var m = center.getMargins();
49324             var centerBox = {
49325                 x: centerX + m.left,
49326                 y: centerY + m.top,
49327                 width: centerW - (m.left+m.right),
49328                 height: centerH - (m.top+m.bottom)
49329             };
49330             //if(this.hideOnLayout){
49331                 //center.el.setStyle("display", "block");
49332             //}
49333             center.updateBox(this.safeBox(centerBox));
49334         }
49335         this.el.repaint();
49336         this.fireEvent("layout", this);
49337     },
49338
49339     // private
49340     safeBox : function(box){
49341         box.width = Math.max(0, box.width);
49342         box.height = Math.max(0, box.height);
49343         return box;
49344     },
49345
49346     /**
49347      * Adds a ContentPanel (or subclass) to this layout.
49348      * @param {String} target The target region key (north, south, east, west or center).
49349      * @param {Roo.ContentPanel} panel The panel to add
49350      * @return {Roo.ContentPanel} The added panel
49351      */
49352     add : function(target, panel){
49353          
49354         target = target.toLowerCase();
49355         return this.regions[target].add(panel);
49356     },
49357
49358     /**
49359      * Remove a ContentPanel (or subclass) to this layout.
49360      * @param {String} target The target region key (north, south, east, west or center).
49361      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49362      * @return {Roo.ContentPanel} The removed panel
49363      */
49364     remove : function(target, panel){
49365         target = target.toLowerCase();
49366         return this.regions[target].remove(panel);
49367     },
49368
49369     /**
49370      * Searches all regions for a panel with the specified id
49371      * @param {String} panelId
49372      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49373      */
49374     findPanel : function(panelId){
49375         var rs = this.regions;
49376         for(var target in rs){
49377             if(typeof rs[target] != "function"){
49378                 var p = rs[target].getPanel(panelId);
49379                 if(p){
49380                     return p;
49381                 }
49382             }
49383         }
49384         return null;
49385     },
49386
49387     /**
49388      * Searches all regions for a panel with the specified id and activates (shows) it.
49389      * @param {String/ContentPanel} panelId The panels id or the panel itself
49390      * @return {Roo.ContentPanel} The shown panel or null
49391      */
49392     showPanel : function(panelId) {
49393       var rs = this.regions;
49394       for(var target in rs){
49395          var r = rs[target];
49396          if(typeof r != "function"){
49397             if(r.hasPanel(panelId)){
49398                return r.showPanel(panelId);
49399             }
49400          }
49401       }
49402       return null;
49403    },
49404
49405    /**
49406      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49407      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49408      */
49409     restoreState : function(provider){
49410         if(!provider){
49411             provider = Roo.state.Manager;
49412         }
49413         var sm = new Roo.LayoutStateManager();
49414         sm.init(this, provider);
49415     },
49416
49417     /**
49418      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49419      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49420      * a valid ContentPanel config object.  Example:
49421      * <pre><code>
49422 // Create the main layout
49423 var layout = new Roo.BorderLayout('main-ct', {
49424     west: {
49425         split:true,
49426         minSize: 175,
49427         titlebar: true
49428     },
49429     center: {
49430         title:'Components'
49431     }
49432 }, 'main-ct');
49433
49434 // Create and add multiple ContentPanels at once via configs
49435 layout.batchAdd({
49436    west: {
49437        id: 'source-files',
49438        autoCreate:true,
49439        title:'Ext Source Files',
49440        autoScroll:true,
49441        fitToFrame:true
49442    },
49443    center : {
49444        el: cview,
49445        autoScroll:true,
49446        fitToFrame:true,
49447        toolbar: tb,
49448        resizeEl:'cbody'
49449    }
49450 });
49451 </code></pre>
49452      * @param {Object} regions An object containing ContentPanel configs by region name
49453      */
49454     batchAdd : function(regions){
49455         this.beginUpdate();
49456         for(var rname in regions){
49457             var lr = this.regions[rname];
49458             if(lr){
49459                 this.addTypedPanels(lr, regions[rname]);
49460             }
49461         }
49462         this.endUpdate();
49463     },
49464
49465     // private
49466     addTypedPanels : function(lr, ps){
49467         if(typeof ps == 'string'){
49468             lr.add(new Roo.ContentPanel(ps));
49469         }
49470         else if(ps instanceof Array){
49471             for(var i =0, len = ps.length; i < len; i++){
49472                 this.addTypedPanels(lr, ps[i]);
49473             }
49474         }
49475         else if(!ps.events){ // raw config?
49476             var el = ps.el;
49477             delete ps.el; // prevent conflict
49478             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49479         }
49480         else {  // panel object assumed!
49481             lr.add(ps);
49482         }
49483     },
49484     /**
49485      * Adds a xtype elements to the layout.
49486      * <pre><code>
49487
49488 layout.addxtype({
49489        xtype : 'ContentPanel',
49490        region: 'west',
49491        items: [ .... ]
49492    }
49493 );
49494
49495 layout.addxtype({
49496         xtype : 'NestedLayoutPanel',
49497         region: 'west',
49498         layout: {
49499            center: { },
49500            west: { }   
49501         },
49502         items : [ ... list of content panels or nested layout panels.. ]
49503    }
49504 );
49505 </code></pre>
49506      * @param {Object} cfg Xtype definition of item to add.
49507      */
49508     addxtype : function(cfg)
49509     {
49510         // basically accepts a pannel...
49511         // can accept a layout region..!?!?
49512         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49513         
49514         if (!cfg.xtype.match(/Panel$/)) {
49515             return false;
49516         }
49517         var ret = false;
49518         
49519         if (typeof(cfg.region) == 'undefined') {
49520             Roo.log("Failed to add Panel, region was not set");
49521             Roo.log(cfg);
49522             return false;
49523         }
49524         var region = cfg.region;
49525         delete cfg.region;
49526         
49527           
49528         var xitems = [];
49529         if (cfg.items) {
49530             xitems = cfg.items;
49531             delete cfg.items;
49532         }
49533         var nb = false;
49534         
49535         switch(cfg.xtype) 
49536         {
49537             case 'ContentPanel':  // ContentPanel (el, cfg)
49538             case 'ScrollPanel':  // ContentPanel (el, cfg)
49539             case 'ViewPanel': 
49540                 if(cfg.autoCreate) {
49541                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49542                 } else {
49543                     var el = this.el.createChild();
49544                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49545                 }
49546                 
49547                 this.add(region, ret);
49548                 break;
49549             
49550             
49551             case 'TreePanel': // our new panel!
49552                 cfg.el = this.el.createChild();
49553                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49554                 this.add(region, ret);
49555                 break;
49556             
49557             case 'NestedLayoutPanel': 
49558                 // create a new Layout (which is  a Border Layout...
49559                 var el = this.el.createChild();
49560                 var clayout = cfg.layout;
49561                 delete cfg.layout;
49562                 clayout.items   = clayout.items  || [];
49563                 // replace this exitems with the clayout ones..
49564                 xitems = clayout.items;
49565                  
49566                 
49567                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49568                     cfg.background = false;
49569                 }
49570                 var layout = new Roo.BorderLayout(el, clayout);
49571                 
49572                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49573                 //console.log('adding nested layout panel '  + cfg.toSource());
49574                 this.add(region, ret);
49575                 nb = {}; /// find first...
49576                 break;
49577                 
49578             case 'GridPanel': 
49579             
49580                 // needs grid and region
49581                 
49582                 //var el = this.getRegion(region).el.createChild();
49583                 var el = this.el.createChild();
49584                 // create the grid first...
49585                 
49586                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49587                 delete cfg.grid;
49588                 if (region == 'center' && this.active ) {
49589                     cfg.background = false;
49590                 }
49591                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49592                 
49593                 this.add(region, ret);
49594                 if (cfg.background) {
49595                     ret.on('activate', function(gp) {
49596                         if (!gp.grid.rendered) {
49597                             gp.grid.render();
49598                         }
49599                     });
49600                 } else {
49601                     grid.render();
49602                 }
49603                 break;
49604            
49605            
49606            
49607                 
49608                 
49609                 
49610             default:
49611                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49612                     
49613                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49614                     this.add(region, ret);
49615                 } else {
49616                 
49617                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49618                     return null;
49619                 }
49620                 
49621              // GridPanel (grid, cfg)
49622             
49623         }
49624         this.beginUpdate();
49625         // add children..
49626         var region = '';
49627         var abn = {};
49628         Roo.each(xitems, function(i)  {
49629             region = nb && i.region ? i.region : false;
49630             
49631             var add = ret.addxtype(i);
49632            
49633             if (region) {
49634                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49635                 if (!i.background) {
49636                     abn[region] = nb[region] ;
49637                 }
49638             }
49639             
49640         });
49641         this.endUpdate();
49642
49643         // make the last non-background panel active..
49644         //if (nb) { Roo.log(abn); }
49645         if (nb) {
49646             
49647             for(var r in abn) {
49648                 region = this.getRegion(r);
49649                 if (region) {
49650                     // tried using nb[r], but it does not work..
49651                      
49652                     region.showPanel(abn[r]);
49653                    
49654                 }
49655             }
49656         }
49657         return ret;
49658         
49659     }
49660 });
49661
49662 /**
49663  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49664  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49665  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49666  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49667  * <pre><code>
49668 // shorthand
49669 var CP = Roo.ContentPanel;
49670
49671 var layout = Roo.BorderLayout.create({
49672     north: {
49673         initialSize: 25,
49674         titlebar: false,
49675         panels: [new CP("north", "North")]
49676     },
49677     west: {
49678         split:true,
49679         initialSize: 200,
49680         minSize: 175,
49681         maxSize: 400,
49682         titlebar: true,
49683         collapsible: true,
49684         panels: [new CP("west", {title: "West"})]
49685     },
49686     east: {
49687         split:true,
49688         initialSize: 202,
49689         minSize: 175,
49690         maxSize: 400,
49691         titlebar: true,
49692         collapsible: true,
49693         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49694     },
49695     south: {
49696         split:true,
49697         initialSize: 100,
49698         minSize: 100,
49699         maxSize: 200,
49700         titlebar: true,
49701         collapsible: true,
49702         panels: [new CP("south", {title: "South", closable: true})]
49703     },
49704     center: {
49705         titlebar: true,
49706         autoScroll:true,
49707         resizeTabs: true,
49708         minTabWidth: 50,
49709         preferredTabWidth: 150,
49710         panels: [
49711             new CP("center1", {title: "Close Me", closable: true}),
49712             new CP("center2", {title: "Center Panel", closable: false})
49713         ]
49714     }
49715 }, document.body);
49716
49717 layout.getRegion("center").showPanel("center1");
49718 </code></pre>
49719  * @param config
49720  * @param targetEl
49721  */
49722 Roo.BorderLayout.create = function(config, targetEl){
49723     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49724     layout.beginUpdate();
49725     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49726     for(var j = 0, jlen = regions.length; j < jlen; j++){
49727         var lr = regions[j];
49728         if(layout.regions[lr] && config[lr].panels){
49729             var r = layout.regions[lr];
49730             var ps = config[lr].panels;
49731             layout.addTypedPanels(r, ps);
49732         }
49733     }
49734     layout.endUpdate();
49735     return layout;
49736 };
49737
49738 // private
49739 Roo.BorderLayout.RegionFactory = {
49740     // private
49741     validRegions : ["north","south","east","west","center"],
49742
49743     // private
49744     create : function(target, mgr, config){
49745         target = target.toLowerCase();
49746         if(config.lightweight || config.basic){
49747             return new Roo.BasicLayoutRegion(mgr, config, target);
49748         }
49749         switch(target){
49750             case "north":
49751                 return new Roo.NorthLayoutRegion(mgr, config);
49752             case "south":
49753                 return new Roo.SouthLayoutRegion(mgr, config);
49754             case "east":
49755                 return new Roo.EastLayoutRegion(mgr, config);
49756             case "west":
49757                 return new Roo.WestLayoutRegion(mgr, config);
49758             case "center":
49759                 return new Roo.CenterLayoutRegion(mgr, config);
49760         }
49761         throw 'Layout region "'+target+'" not supported.';
49762     }
49763 };/*
49764  * Based on:
49765  * Ext JS Library 1.1.1
49766  * Copyright(c) 2006-2007, Ext JS, LLC.
49767  *
49768  * Originally Released Under LGPL - original licence link has changed is not relivant.
49769  *
49770  * Fork - LGPL
49771  * <script type="text/javascript">
49772  */
49773  
49774 /**
49775  * @class Roo.BasicLayoutRegion
49776  * @extends Roo.util.Observable
49777  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49778  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49779  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49780  */
49781 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49782     this.mgr = mgr;
49783     this.position  = pos;
49784     this.events = {
49785         /**
49786          * @scope Roo.BasicLayoutRegion
49787          */
49788         
49789         /**
49790          * @event beforeremove
49791          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49792          * @param {Roo.LayoutRegion} this
49793          * @param {Roo.ContentPanel} panel The panel
49794          * @param {Object} e The cancel event object
49795          */
49796         "beforeremove" : true,
49797         /**
49798          * @event invalidated
49799          * Fires when the layout for this region is changed.
49800          * @param {Roo.LayoutRegion} this
49801          */
49802         "invalidated" : true,
49803         /**
49804          * @event visibilitychange
49805          * Fires when this region is shown or hidden 
49806          * @param {Roo.LayoutRegion} this
49807          * @param {Boolean} visibility true or false
49808          */
49809         "visibilitychange" : true,
49810         /**
49811          * @event paneladded
49812          * Fires when a panel is added. 
49813          * @param {Roo.LayoutRegion} this
49814          * @param {Roo.ContentPanel} panel The panel
49815          */
49816         "paneladded" : true,
49817         /**
49818          * @event panelremoved
49819          * Fires when a panel is removed. 
49820          * @param {Roo.LayoutRegion} this
49821          * @param {Roo.ContentPanel} panel The panel
49822          */
49823         "panelremoved" : true,
49824         /**
49825          * @event collapsed
49826          * Fires when this region is collapsed.
49827          * @param {Roo.LayoutRegion} this
49828          */
49829         "collapsed" : true,
49830         /**
49831          * @event expanded
49832          * Fires when this region is expanded.
49833          * @param {Roo.LayoutRegion} this
49834          */
49835         "expanded" : true,
49836         /**
49837          * @event slideshow
49838          * Fires when this region is slid into view.
49839          * @param {Roo.LayoutRegion} this
49840          */
49841         "slideshow" : true,
49842         /**
49843          * @event slidehide
49844          * Fires when this region slides out of view. 
49845          * @param {Roo.LayoutRegion} this
49846          */
49847         "slidehide" : true,
49848         /**
49849          * @event panelactivated
49850          * Fires when a panel is activated. 
49851          * @param {Roo.LayoutRegion} this
49852          * @param {Roo.ContentPanel} panel The activated panel
49853          */
49854         "panelactivated" : true,
49855         /**
49856          * @event resized
49857          * Fires when the user resizes this region. 
49858          * @param {Roo.LayoutRegion} this
49859          * @param {Number} newSize The new size (width for east/west, height for north/south)
49860          */
49861         "resized" : true
49862     };
49863     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49864     this.panels = new Roo.util.MixedCollection();
49865     this.panels.getKey = this.getPanelId.createDelegate(this);
49866     this.box = null;
49867     this.activePanel = null;
49868     // ensure listeners are added...
49869     
49870     if (config.listeners || config.events) {
49871         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49872             listeners : config.listeners || {},
49873             events : config.events || {}
49874         });
49875     }
49876     
49877     if(skipConfig !== true){
49878         this.applyConfig(config);
49879     }
49880 };
49881
49882 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49883     getPanelId : function(p){
49884         return p.getId();
49885     },
49886     
49887     applyConfig : function(config){
49888         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49889         this.config = config;
49890         
49891     },
49892     
49893     /**
49894      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49895      * the width, for horizontal (north, south) the height.
49896      * @param {Number} newSize The new width or height
49897      */
49898     resizeTo : function(newSize){
49899         var el = this.el ? this.el :
49900                  (this.activePanel ? this.activePanel.getEl() : null);
49901         if(el){
49902             switch(this.position){
49903                 case "east":
49904                 case "west":
49905                     el.setWidth(newSize);
49906                     this.fireEvent("resized", this, newSize);
49907                 break;
49908                 case "north":
49909                 case "south":
49910                     el.setHeight(newSize);
49911                     this.fireEvent("resized", this, newSize);
49912                 break;                
49913             }
49914         }
49915     },
49916     
49917     getBox : function(){
49918         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49919     },
49920     
49921     getMargins : function(){
49922         return this.margins;
49923     },
49924     
49925     updateBox : function(box){
49926         this.box = box;
49927         var el = this.activePanel.getEl();
49928         el.dom.style.left = box.x + "px";
49929         el.dom.style.top = box.y + "px";
49930         this.activePanel.setSize(box.width, box.height);
49931     },
49932     
49933     /**
49934      * Returns the container element for this region.
49935      * @return {Roo.Element}
49936      */
49937     getEl : function(){
49938         return this.activePanel;
49939     },
49940     
49941     /**
49942      * Returns true if this region is currently visible.
49943      * @return {Boolean}
49944      */
49945     isVisible : function(){
49946         return this.activePanel ? true : false;
49947     },
49948     
49949     setActivePanel : function(panel){
49950         panel = this.getPanel(panel);
49951         if(this.activePanel && this.activePanel != panel){
49952             this.activePanel.setActiveState(false);
49953             this.activePanel.getEl().setLeftTop(-10000,-10000);
49954         }
49955         this.activePanel = panel;
49956         panel.setActiveState(true);
49957         if(this.box){
49958             panel.setSize(this.box.width, this.box.height);
49959         }
49960         this.fireEvent("panelactivated", this, panel);
49961         this.fireEvent("invalidated");
49962     },
49963     
49964     /**
49965      * Show the specified panel.
49966      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49967      * @return {Roo.ContentPanel} The shown panel or null
49968      */
49969     showPanel : function(panel){
49970         if(panel = this.getPanel(panel)){
49971             this.setActivePanel(panel);
49972         }
49973         return panel;
49974     },
49975     
49976     /**
49977      * Get the active panel for this region.
49978      * @return {Roo.ContentPanel} The active panel or null
49979      */
49980     getActivePanel : function(){
49981         return this.activePanel;
49982     },
49983     
49984     /**
49985      * Add the passed ContentPanel(s)
49986      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49987      * @return {Roo.ContentPanel} The panel added (if only one was added)
49988      */
49989     add : function(panel){
49990         if(arguments.length > 1){
49991             for(var i = 0, len = arguments.length; i < len; i++) {
49992                 this.add(arguments[i]);
49993             }
49994             return null;
49995         }
49996         if(this.hasPanel(panel)){
49997             this.showPanel(panel);
49998             return panel;
49999         }
50000         var el = panel.getEl();
50001         if(el.dom.parentNode != this.mgr.el.dom){
50002             this.mgr.el.dom.appendChild(el.dom);
50003         }
50004         if(panel.setRegion){
50005             panel.setRegion(this);
50006         }
50007         this.panels.add(panel);
50008         el.setStyle("position", "absolute");
50009         if(!panel.background){
50010             this.setActivePanel(panel);
50011             if(this.config.initialSize && this.panels.getCount()==1){
50012                 this.resizeTo(this.config.initialSize);
50013             }
50014         }
50015         this.fireEvent("paneladded", this, panel);
50016         return panel;
50017     },
50018     
50019     /**
50020      * Returns true if the panel is in this region.
50021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50022      * @return {Boolean}
50023      */
50024     hasPanel : function(panel){
50025         if(typeof panel == "object"){ // must be panel obj
50026             panel = panel.getId();
50027         }
50028         return this.getPanel(panel) ? true : false;
50029     },
50030     
50031     /**
50032      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50034      * @param {Boolean} preservePanel Overrides the config preservePanel option
50035      * @return {Roo.ContentPanel} The panel that was removed
50036      */
50037     remove : function(panel, preservePanel){
50038         panel = this.getPanel(panel);
50039         if(!panel){
50040             return null;
50041         }
50042         var e = {};
50043         this.fireEvent("beforeremove", this, panel, e);
50044         if(e.cancel === true){
50045             return null;
50046         }
50047         var panelId = panel.getId();
50048         this.panels.removeKey(panelId);
50049         return panel;
50050     },
50051     
50052     /**
50053      * Returns the panel specified or null if it's not in this region.
50054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50055      * @return {Roo.ContentPanel}
50056      */
50057     getPanel : function(id){
50058         if(typeof id == "object"){ // must be panel obj
50059             return id;
50060         }
50061         return this.panels.get(id);
50062     },
50063     
50064     /**
50065      * Returns this regions position (north/south/east/west/center).
50066      * @return {String} 
50067      */
50068     getPosition: function(){
50069         return this.position;    
50070     }
50071 });/*
50072  * Based on:
50073  * Ext JS Library 1.1.1
50074  * Copyright(c) 2006-2007, Ext JS, LLC.
50075  *
50076  * Originally Released Under LGPL - original licence link has changed is not relivant.
50077  *
50078  * Fork - LGPL
50079  * <script type="text/javascript">
50080  */
50081  
50082 /**
50083  * @class Roo.LayoutRegion
50084  * @extends Roo.BasicLayoutRegion
50085  * This class represents a region in a layout manager.
50086  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50087  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50088  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50089  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50090  * @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})
50091  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50092  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50093  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50094  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50095  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50096  * @cfg {String}    title           The title for the region (overrides panel titles)
50097  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50098  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50099  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50100  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50101  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50102  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50103  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50104  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50105  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50106  * @cfg {Boolean}   showPin         True to show a pin button
50107  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50108  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50109  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50110  * @cfg {Number}    width           For East/West panels
50111  * @cfg {Number}    height          For North/South panels
50112  * @cfg {Boolean}   split           To show the splitter
50113  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50114  */
50115 Roo.LayoutRegion = function(mgr, config, pos){
50116     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50117     var dh = Roo.DomHelper;
50118     /** This region's container element 
50119     * @type Roo.Element */
50120     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50121     /** This region's title element 
50122     * @type Roo.Element */
50123
50124     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50125         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50126         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50127     ]}, true);
50128     this.titleEl.enableDisplayMode();
50129     /** This region's title text element 
50130     * @type HTMLElement */
50131     this.titleTextEl = this.titleEl.dom.firstChild;
50132     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50133     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50134     this.closeBtn.enableDisplayMode();
50135     this.closeBtn.on("click", this.closeClicked, this);
50136     this.closeBtn.hide();
50137
50138     this.createBody(config);
50139     this.visible = true;
50140     this.collapsed = false;
50141
50142     if(config.hideWhenEmpty){
50143         this.hide();
50144         this.on("paneladded", this.validateVisibility, this);
50145         this.on("panelremoved", this.validateVisibility, this);
50146     }
50147     this.applyConfig(config);
50148 };
50149
50150 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50151
50152     createBody : function(){
50153         /** This region's body element 
50154         * @type Roo.Element */
50155         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50156     },
50157
50158     applyConfig : function(c){
50159         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50160             var dh = Roo.DomHelper;
50161             if(c.titlebar !== false){
50162                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50163                 this.collapseBtn.on("click", this.collapse, this);
50164                 this.collapseBtn.enableDisplayMode();
50165
50166                 if(c.showPin === true || this.showPin){
50167                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50168                     this.stickBtn.enableDisplayMode();
50169                     this.stickBtn.on("click", this.expand, this);
50170                     this.stickBtn.hide();
50171                 }
50172             }
50173             /** This region's collapsed element
50174             * @type Roo.Element */
50175             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50176                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50177             ]}, true);
50178             if(c.floatable !== false){
50179                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50180                this.collapsedEl.on("click", this.collapseClick, this);
50181             }
50182
50183             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50184                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50185                    id: "message", unselectable: "on", style:{"float":"left"}});
50186                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50187              }
50188             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50189             this.expandBtn.on("click", this.expand, this);
50190         }
50191         if(this.collapseBtn){
50192             this.collapseBtn.setVisible(c.collapsible == true);
50193         }
50194         this.cmargins = c.cmargins || this.cmargins ||
50195                          (this.position == "west" || this.position == "east" ?
50196                              {top: 0, left: 2, right:2, bottom: 0} :
50197                              {top: 2, left: 0, right:0, bottom: 2});
50198         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50199         this.bottomTabs = c.tabPosition != "top";
50200         this.autoScroll = c.autoScroll || false;
50201         if(this.autoScroll){
50202             this.bodyEl.setStyle("overflow", "auto");
50203         }else{
50204             this.bodyEl.setStyle("overflow", "hidden");
50205         }
50206         //if(c.titlebar !== false){
50207             if((!c.titlebar && !c.title) || c.titlebar === false){
50208                 this.titleEl.hide();
50209             }else{
50210                 this.titleEl.show();
50211                 if(c.title){
50212                     this.titleTextEl.innerHTML = c.title;
50213                 }
50214             }
50215         //}
50216         this.duration = c.duration || .30;
50217         this.slideDuration = c.slideDuration || .45;
50218         this.config = c;
50219         if(c.collapsed){
50220             this.collapse(true);
50221         }
50222         if(c.hidden){
50223             this.hide();
50224         }
50225     },
50226     /**
50227      * Returns true if this region is currently visible.
50228      * @return {Boolean}
50229      */
50230     isVisible : function(){
50231         return this.visible;
50232     },
50233
50234     /**
50235      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50236      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50237      */
50238     setCollapsedTitle : function(title){
50239         title = title || "&#160;";
50240         if(this.collapsedTitleTextEl){
50241             this.collapsedTitleTextEl.innerHTML = title;
50242         }
50243     },
50244
50245     getBox : function(){
50246         var b;
50247         if(!this.collapsed){
50248             b = this.el.getBox(false, true);
50249         }else{
50250             b = this.collapsedEl.getBox(false, true);
50251         }
50252         return b;
50253     },
50254
50255     getMargins : function(){
50256         return this.collapsed ? this.cmargins : this.margins;
50257     },
50258
50259     highlight : function(){
50260         this.el.addClass("x-layout-panel-dragover");
50261     },
50262
50263     unhighlight : function(){
50264         this.el.removeClass("x-layout-panel-dragover");
50265     },
50266
50267     updateBox : function(box){
50268         this.box = box;
50269         if(!this.collapsed){
50270             this.el.dom.style.left = box.x + "px";
50271             this.el.dom.style.top = box.y + "px";
50272             this.updateBody(box.width, box.height);
50273         }else{
50274             this.collapsedEl.dom.style.left = box.x + "px";
50275             this.collapsedEl.dom.style.top = box.y + "px";
50276             this.collapsedEl.setSize(box.width, box.height);
50277         }
50278         if(this.tabs){
50279             this.tabs.autoSizeTabs();
50280         }
50281     },
50282
50283     updateBody : function(w, h){
50284         if(w !== null){
50285             this.el.setWidth(w);
50286             w -= this.el.getBorderWidth("rl");
50287             if(this.config.adjustments){
50288                 w += this.config.adjustments[0];
50289             }
50290         }
50291         if(h !== null){
50292             this.el.setHeight(h);
50293             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50294             h -= this.el.getBorderWidth("tb");
50295             if(this.config.adjustments){
50296                 h += this.config.adjustments[1];
50297             }
50298             this.bodyEl.setHeight(h);
50299             if(this.tabs){
50300                 h = this.tabs.syncHeight(h);
50301             }
50302         }
50303         if(this.panelSize){
50304             w = w !== null ? w : this.panelSize.width;
50305             h = h !== null ? h : this.panelSize.height;
50306         }
50307         if(this.activePanel){
50308             var el = this.activePanel.getEl();
50309             w = w !== null ? w : el.getWidth();
50310             h = h !== null ? h : el.getHeight();
50311             this.panelSize = {width: w, height: h};
50312             this.activePanel.setSize(w, h);
50313         }
50314         if(Roo.isIE && this.tabs){
50315             this.tabs.el.repaint();
50316         }
50317     },
50318
50319     /**
50320      * Returns the container element for this region.
50321      * @return {Roo.Element}
50322      */
50323     getEl : function(){
50324         return this.el;
50325     },
50326
50327     /**
50328      * Hides this region.
50329      */
50330     hide : function(){
50331         if(!this.collapsed){
50332             this.el.dom.style.left = "-2000px";
50333             this.el.hide();
50334         }else{
50335             this.collapsedEl.dom.style.left = "-2000px";
50336             this.collapsedEl.hide();
50337         }
50338         this.visible = false;
50339         this.fireEvent("visibilitychange", this, false);
50340     },
50341
50342     /**
50343      * Shows this region if it was previously hidden.
50344      */
50345     show : function(){
50346         if(!this.collapsed){
50347             this.el.show();
50348         }else{
50349             this.collapsedEl.show();
50350         }
50351         this.visible = true;
50352         this.fireEvent("visibilitychange", this, true);
50353     },
50354
50355     closeClicked : function(){
50356         if(this.activePanel){
50357             this.remove(this.activePanel);
50358         }
50359     },
50360
50361     collapseClick : function(e){
50362         if(this.isSlid){
50363            e.stopPropagation();
50364            this.slideIn();
50365         }else{
50366            e.stopPropagation();
50367            this.slideOut();
50368         }
50369     },
50370
50371     /**
50372      * Collapses this region.
50373      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50374      */
50375     collapse : function(skipAnim){
50376         if(this.collapsed) return;
50377         this.collapsed = true;
50378         if(this.split){
50379             this.split.el.hide();
50380         }
50381         if(this.config.animate && skipAnim !== true){
50382             this.fireEvent("invalidated", this);
50383             this.animateCollapse();
50384         }else{
50385             this.el.setLocation(-20000,-20000);
50386             this.el.hide();
50387             this.collapsedEl.show();
50388             this.fireEvent("collapsed", this);
50389             this.fireEvent("invalidated", this);
50390         }
50391     },
50392
50393     animateCollapse : function(){
50394         // overridden
50395     },
50396
50397     /**
50398      * Expands this region if it was previously collapsed.
50399      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50400      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50401      */
50402     expand : function(e, skipAnim){
50403         if(e) e.stopPropagation();
50404         if(!this.collapsed || this.el.hasActiveFx()) return;
50405         if(this.isSlid){
50406             this.afterSlideIn();
50407             skipAnim = true;
50408         }
50409         this.collapsed = false;
50410         if(this.config.animate && skipAnim !== true){
50411             this.animateExpand();
50412         }else{
50413             this.el.show();
50414             if(this.split){
50415                 this.split.el.show();
50416             }
50417             this.collapsedEl.setLocation(-2000,-2000);
50418             this.collapsedEl.hide();
50419             this.fireEvent("invalidated", this);
50420             this.fireEvent("expanded", this);
50421         }
50422     },
50423
50424     animateExpand : function(){
50425         // overridden
50426     },
50427
50428     initTabs : function()
50429     {
50430         this.bodyEl.setStyle("overflow", "hidden");
50431         var ts = new Roo.TabPanel(
50432                 this.bodyEl.dom,
50433                 {
50434                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50435                     disableTooltips: this.config.disableTabTips,
50436                     toolbar : this.config.toolbar
50437                 }
50438         );
50439         if(this.config.hideTabs){
50440             ts.stripWrap.setDisplayed(false);
50441         }
50442         this.tabs = ts;
50443         ts.resizeTabs = this.config.resizeTabs === true;
50444         ts.minTabWidth = this.config.minTabWidth || 40;
50445         ts.maxTabWidth = this.config.maxTabWidth || 250;
50446         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50447         ts.monitorResize = false;
50448         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50449         ts.bodyEl.addClass('x-layout-tabs-body');
50450         this.panels.each(this.initPanelAsTab, this);
50451     },
50452
50453     initPanelAsTab : function(panel){
50454         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50455                     this.config.closeOnTab && panel.isClosable());
50456         if(panel.tabTip !== undefined){
50457             ti.setTooltip(panel.tabTip);
50458         }
50459         ti.on("activate", function(){
50460               this.setActivePanel(panel);
50461         }, this);
50462         if(this.config.closeOnTab){
50463             ti.on("beforeclose", function(t, e){
50464                 e.cancel = true;
50465                 this.remove(panel);
50466             }, this);
50467         }
50468         return ti;
50469     },
50470
50471     updatePanelTitle : function(panel, title){
50472         if(this.activePanel == panel){
50473             this.updateTitle(title);
50474         }
50475         if(this.tabs){
50476             var ti = this.tabs.getTab(panel.getEl().id);
50477             ti.setText(title);
50478             if(panel.tabTip !== undefined){
50479                 ti.setTooltip(panel.tabTip);
50480             }
50481         }
50482     },
50483
50484     updateTitle : function(title){
50485         if(this.titleTextEl && !this.config.title){
50486             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50487         }
50488     },
50489
50490     setActivePanel : function(panel){
50491         panel = this.getPanel(panel);
50492         if(this.activePanel && this.activePanel != panel){
50493             this.activePanel.setActiveState(false);
50494         }
50495         this.activePanel = panel;
50496         panel.setActiveState(true);
50497         if(this.panelSize){
50498             panel.setSize(this.panelSize.width, this.panelSize.height);
50499         }
50500         if(this.closeBtn){
50501             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50502         }
50503         this.updateTitle(panel.getTitle());
50504         if(this.tabs){
50505             this.fireEvent("invalidated", this);
50506         }
50507         this.fireEvent("panelactivated", this, panel);
50508     },
50509
50510     /**
50511      * Shows the specified panel.
50512      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50513      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50514      */
50515     showPanel : function(panel)
50516     {
50517         panel = this.getPanel(panel);
50518         if(panel){
50519             if(this.tabs){
50520                 var tab = this.tabs.getTab(panel.getEl().id);
50521                 if(tab.isHidden()){
50522                     this.tabs.unhideTab(tab.id);
50523                 }
50524                 tab.activate();
50525             }else{
50526                 this.setActivePanel(panel);
50527             }
50528         }
50529         return panel;
50530     },
50531
50532     /**
50533      * Get the active panel for this region.
50534      * @return {Roo.ContentPanel} The active panel or null
50535      */
50536     getActivePanel : function(){
50537         return this.activePanel;
50538     },
50539
50540     validateVisibility : function(){
50541         if(this.panels.getCount() < 1){
50542             this.updateTitle("&#160;");
50543             this.closeBtn.hide();
50544             this.hide();
50545         }else{
50546             if(!this.isVisible()){
50547                 this.show();
50548             }
50549         }
50550     },
50551
50552     /**
50553      * Adds the passed ContentPanel(s) to this region.
50554      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50555      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50556      */
50557     add : function(panel){
50558         if(arguments.length > 1){
50559             for(var i = 0, len = arguments.length; i < len; i++) {
50560                 this.add(arguments[i]);
50561             }
50562             return null;
50563         }
50564         if(this.hasPanel(panel)){
50565             this.showPanel(panel);
50566             return panel;
50567         }
50568         panel.setRegion(this);
50569         this.panels.add(panel);
50570         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50571             this.bodyEl.dom.appendChild(panel.getEl().dom);
50572             if(panel.background !== true){
50573                 this.setActivePanel(panel);
50574             }
50575             this.fireEvent("paneladded", this, panel);
50576             return panel;
50577         }
50578         if(!this.tabs){
50579             this.initTabs();
50580         }else{
50581             this.initPanelAsTab(panel);
50582         }
50583         if(panel.background !== true){
50584             this.tabs.activate(panel.getEl().id);
50585         }
50586         this.fireEvent("paneladded", this, panel);
50587         return panel;
50588     },
50589
50590     /**
50591      * Hides the tab for the specified panel.
50592      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50593      */
50594     hidePanel : function(panel){
50595         if(this.tabs && (panel = this.getPanel(panel))){
50596             this.tabs.hideTab(panel.getEl().id);
50597         }
50598     },
50599
50600     /**
50601      * Unhides the tab for a previously hidden panel.
50602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50603      */
50604     unhidePanel : function(panel){
50605         if(this.tabs && (panel = this.getPanel(panel))){
50606             this.tabs.unhideTab(panel.getEl().id);
50607         }
50608     },
50609
50610     clearPanels : function(){
50611         while(this.panels.getCount() > 0){
50612              this.remove(this.panels.first());
50613         }
50614     },
50615
50616     /**
50617      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50618      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50619      * @param {Boolean} preservePanel Overrides the config preservePanel option
50620      * @return {Roo.ContentPanel} The panel that was removed
50621      */
50622     remove : function(panel, preservePanel){
50623         panel = this.getPanel(panel);
50624         if(!panel){
50625             return null;
50626         }
50627         var e = {};
50628         this.fireEvent("beforeremove", this, panel, e);
50629         if(e.cancel === true){
50630             return null;
50631         }
50632         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50633         var panelId = panel.getId();
50634         this.panels.removeKey(panelId);
50635         if(preservePanel){
50636             document.body.appendChild(panel.getEl().dom);
50637         }
50638         if(this.tabs){
50639             this.tabs.removeTab(panel.getEl().id);
50640         }else if (!preservePanel){
50641             this.bodyEl.dom.removeChild(panel.getEl().dom);
50642         }
50643         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50644             var p = this.panels.first();
50645             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50646             tempEl.appendChild(p.getEl().dom);
50647             this.bodyEl.update("");
50648             this.bodyEl.dom.appendChild(p.getEl().dom);
50649             tempEl = null;
50650             this.updateTitle(p.getTitle());
50651             this.tabs = null;
50652             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50653             this.setActivePanel(p);
50654         }
50655         panel.setRegion(null);
50656         if(this.activePanel == panel){
50657             this.activePanel = null;
50658         }
50659         if(this.config.autoDestroy !== false && preservePanel !== true){
50660             try{panel.destroy();}catch(e){}
50661         }
50662         this.fireEvent("panelremoved", this, panel);
50663         return panel;
50664     },
50665
50666     /**
50667      * Returns the TabPanel component used by this region
50668      * @return {Roo.TabPanel}
50669      */
50670     getTabs : function(){
50671         return this.tabs;
50672     },
50673
50674     createTool : function(parentEl, className){
50675         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50676             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50677         btn.addClassOnOver("x-layout-tools-button-over");
50678         return btn;
50679     }
50680 });/*
50681  * Based on:
50682  * Ext JS Library 1.1.1
50683  * Copyright(c) 2006-2007, Ext JS, LLC.
50684  *
50685  * Originally Released Under LGPL - original licence link has changed is not relivant.
50686  *
50687  * Fork - LGPL
50688  * <script type="text/javascript">
50689  */
50690  
50691
50692
50693 /**
50694  * @class Roo.SplitLayoutRegion
50695  * @extends Roo.LayoutRegion
50696  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50697  */
50698 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50699     this.cursor = cursor;
50700     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50701 };
50702
50703 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50704     splitTip : "Drag to resize.",
50705     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50706     useSplitTips : false,
50707
50708     applyConfig : function(config){
50709         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50710         if(config.split){
50711             if(!this.split){
50712                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50713                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50714                 /** The SplitBar for this region 
50715                 * @type Roo.SplitBar */
50716                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50717                 this.split.on("moved", this.onSplitMove, this);
50718                 this.split.useShim = config.useShim === true;
50719                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50720                 if(this.useSplitTips){
50721                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50722                 }
50723                 if(config.collapsible){
50724                     this.split.el.on("dblclick", this.collapse,  this);
50725                 }
50726             }
50727             if(typeof config.minSize != "undefined"){
50728                 this.split.minSize = config.minSize;
50729             }
50730             if(typeof config.maxSize != "undefined"){
50731                 this.split.maxSize = config.maxSize;
50732             }
50733             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50734                 this.hideSplitter();
50735             }
50736         }
50737     },
50738
50739     getHMaxSize : function(){
50740          var cmax = this.config.maxSize || 10000;
50741          var center = this.mgr.getRegion("center");
50742          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50743     },
50744
50745     getVMaxSize : function(){
50746          var cmax = this.config.maxSize || 10000;
50747          var center = this.mgr.getRegion("center");
50748          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50749     },
50750
50751     onSplitMove : function(split, newSize){
50752         this.fireEvent("resized", this, newSize);
50753     },
50754     
50755     /** 
50756      * Returns the {@link Roo.SplitBar} for this region.
50757      * @return {Roo.SplitBar}
50758      */
50759     getSplitBar : function(){
50760         return this.split;
50761     },
50762     
50763     hide : function(){
50764         this.hideSplitter();
50765         Roo.SplitLayoutRegion.superclass.hide.call(this);
50766     },
50767
50768     hideSplitter : function(){
50769         if(this.split){
50770             this.split.el.setLocation(-2000,-2000);
50771             this.split.el.hide();
50772         }
50773     },
50774
50775     show : function(){
50776         if(this.split){
50777             this.split.el.show();
50778         }
50779         Roo.SplitLayoutRegion.superclass.show.call(this);
50780     },
50781     
50782     beforeSlide: function(){
50783         if(Roo.isGecko){// firefox overflow auto bug workaround
50784             this.bodyEl.clip();
50785             if(this.tabs) this.tabs.bodyEl.clip();
50786             if(this.activePanel){
50787                 this.activePanel.getEl().clip();
50788                 
50789                 if(this.activePanel.beforeSlide){
50790                     this.activePanel.beforeSlide();
50791                 }
50792             }
50793         }
50794     },
50795     
50796     afterSlide : function(){
50797         if(Roo.isGecko){// firefox overflow auto bug workaround
50798             this.bodyEl.unclip();
50799             if(this.tabs) this.tabs.bodyEl.unclip();
50800             if(this.activePanel){
50801                 this.activePanel.getEl().unclip();
50802                 if(this.activePanel.afterSlide){
50803                     this.activePanel.afterSlide();
50804                 }
50805             }
50806         }
50807     },
50808
50809     initAutoHide : function(){
50810         if(this.autoHide !== false){
50811             if(!this.autoHideHd){
50812                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50813                 this.autoHideHd = {
50814                     "mouseout": function(e){
50815                         if(!e.within(this.el, true)){
50816                             st.delay(500);
50817                         }
50818                     },
50819                     "mouseover" : function(e){
50820                         st.cancel();
50821                     },
50822                     scope : this
50823                 };
50824             }
50825             this.el.on(this.autoHideHd);
50826         }
50827     },
50828
50829     clearAutoHide : function(){
50830         if(this.autoHide !== false){
50831             this.el.un("mouseout", this.autoHideHd.mouseout);
50832             this.el.un("mouseover", this.autoHideHd.mouseover);
50833         }
50834     },
50835
50836     clearMonitor : function(){
50837         Roo.get(document).un("click", this.slideInIf, this);
50838     },
50839
50840     // these names are backwards but not changed for compat
50841     slideOut : function(){
50842         if(this.isSlid || this.el.hasActiveFx()){
50843             return;
50844         }
50845         this.isSlid = true;
50846         if(this.collapseBtn){
50847             this.collapseBtn.hide();
50848         }
50849         this.closeBtnState = this.closeBtn.getStyle('display');
50850         this.closeBtn.hide();
50851         if(this.stickBtn){
50852             this.stickBtn.show();
50853         }
50854         this.el.show();
50855         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50856         this.beforeSlide();
50857         this.el.setStyle("z-index", 10001);
50858         this.el.slideIn(this.getSlideAnchor(), {
50859             callback: function(){
50860                 this.afterSlide();
50861                 this.initAutoHide();
50862                 Roo.get(document).on("click", this.slideInIf, this);
50863                 this.fireEvent("slideshow", this);
50864             },
50865             scope: this,
50866             block: true
50867         });
50868     },
50869
50870     afterSlideIn : function(){
50871         this.clearAutoHide();
50872         this.isSlid = false;
50873         this.clearMonitor();
50874         this.el.setStyle("z-index", "");
50875         if(this.collapseBtn){
50876             this.collapseBtn.show();
50877         }
50878         this.closeBtn.setStyle('display', this.closeBtnState);
50879         if(this.stickBtn){
50880             this.stickBtn.hide();
50881         }
50882         this.fireEvent("slidehide", this);
50883     },
50884
50885     slideIn : function(cb){
50886         if(!this.isSlid || this.el.hasActiveFx()){
50887             Roo.callback(cb);
50888             return;
50889         }
50890         this.isSlid = false;
50891         this.beforeSlide();
50892         this.el.slideOut(this.getSlideAnchor(), {
50893             callback: function(){
50894                 this.el.setLeftTop(-10000, -10000);
50895                 this.afterSlide();
50896                 this.afterSlideIn();
50897                 Roo.callback(cb);
50898             },
50899             scope: this,
50900             block: true
50901         });
50902     },
50903     
50904     slideInIf : function(e){
50905         if(!e.within(this.el)){
50906             this.slideIn();
50907         }
50908     },
50909
50910     animateCollapse : function(){
50911         this.beforeSlide();
50912         this.el.setStyle("z-index", 20000);
50913         var anchor = this.getSlideAnchor();
50914         this.el.slideOut(anchor, {
50915             callback : function(){
50916                 this.el.setStyle("z-index", "");
50917                 this.collapsedEl.slideIn(anchor, {duration:.3});
50918                 this.afterSlide();
50919                 this.el.setLocation(-10000,-10000);
50920                 this.el.hide();
50921                 this.fireEvent("collapsed", this);
50922             },
50923             scope: this,
50924             block: true
50925         });
50926     },
50927
50928     animateExpand : function(){
50929         this.beforeSlide();
50930         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50931         this.el.setStyle("z-index", 20000);
50932         this.collapsedEl.hide({
50933             duration:.1
50934         });
50935         this.el.slideIn(this.getSlideAnchor(), {
50936             callback : function(){
50937                 this.el.setStyle("z-index", "");
50938                 this.afterSlide();
50939                 if(this.split){
50940                     this.split.el.show();
50941                 }
50942                 this.fireEvent("invalidated", this);
50943                 this.fireEvent("expanded", this);
50944             },
50945             scope: this,
50946             block: true
50947         });
50948     },
50949
50950     anchors : {
50951         "west" : "left",
50952         "east" : "right",
50953         "north" : "top",
50954         "south" : "bottom"
50955     },
50956
50957     sanchors : {
50958         "west" : "l",
50959         "east" : "r",
50960         "north" : "t",
50961         "south" : "b"
50962     },
50963
50964     canchors : {
50965         "west" : "tl-tr",
50966         "east" : "tr-tl",
50967         "north" : "tl-bl",
50968         "south" : "bl-tl"
50969     },
50970
50971     getAnchor : function(){
50972         return this.anchors[this.position];
50973     },
50974
50975     getCollapseAnchor : function(){
50976         return this.canchors[this.position];
50977     },
50978
50979     getSlideAnchor : function(){
50980         return this.sanchors[this.position];
50981     },
50982
50983     getAlignAdj : function(){
50984         var cm = this.cmargins;
50985         switch(this.position){
50986             case "west":
50987                 return [0, 0];
50988             break;
50989             case "east":
50990                 return [0, 0];
50991             break;
50992             case "north":
50993                 return [0, 0];
50994             break;
50995             case "south":
50996                 return [0, 0];
50997             break;
50998         }
50999     },
51000
51001     getExpandAdj : function(){
51002         var c = this.collapsedEl, cm = this.cmargins;
51003         switch(this.position){
51004             case "west":
51005                 return [-(cm.right+c.getWidth()+cm.left), 0];
51006             break;
51007             case "east":
51008                 return [cm.right+c.getWidth()+cm.left, 0];
51009             break;
51010             case "north":
51011                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51012             break;
51013             case "south":
51014                 return [0, cm.top+cm.bottom+c.getHeight()];
51015             break;
51016         }
51017     }
51018 });/*
51019  * Based on:
51020  * Ext JS Library 1.1.1
51021  * Copyright(c) 2006-2007, Ext JS, LLC.
51022  *
51023  * Originally Released Under LGPL - original licence link has changed is not relivant.
51024  *
51025  * Fork - LGPL
51026  * <script type="text/javascript">
51027  */
51028 /*
51029  * These classes are private internal classes
51030  */
51031 Roo.CenterLayoutRegion = function(mgr, config){
51032     Roo.LayoutRegion.call(this, mgr, config, "center");
51033     this.visible = true;
51034     this.minWidth = config.minWidth || 20;
51035     this.minHeight = config.minHeight || 20;
51036 };
51037
51038 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51039     hide : function(){
51040         // center panel can't be hidden
51041     },
51042     
51043     show : function(){
51044         // center panel can't be hidden
51045     },
51046     
51047     getMinWidth: function(){
51048         return this.minWidth;
51049     },
51050     
51051     getMinHeight: function(){
51052         return this.minHeight;
51053     }
51054 });
51055
51056
51057 Roo.NorthLayoutRegion = function(mgr, config){
51058     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51059     if(this.split){
51060         this.split.placement = Roo.SplitBar.TOP;
51061         this.split.orientation = Roo.SplitBar.VERTICAL;
51062         this.split.el.addClass("x-layout-split-v");
51063     }
51064     var size = config.initialSize || config.height;
51065     if(typeof size != "undefined"){
51066         this.el.setHeight(size);
51067     }
51068 };
51069 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51070     orientation: Roo.SplitBar.VERTICAL,
51071     getBox : function(){
51072         if(this.collapsed){
51073             return this.collapsedEl.getBox();
51074         }
51075         var box = this.el.getBox();
51076         if(this.split){
51077             box.height += this.split.el.getHeight();
51078         }
51079         return box;
51080     },
51081     
51082     updateBox : function(box){
51083         if(this.split && !this.collapsed){
51084             box.height -= this.split.el.getHeight();
51085             this.split.el.setLeft(box.x);
51086             this.split.el.setTop(box.y+box.height);
51087             this.split.el.setWidth(box.width);
51088         }
51089         if(this.collapsed){
51090             this.updateBody(box.width, null);
51091         }
51092         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51093     }
51094 });
51095
51096 Roo.SouthLayoutRegion = function(mgr, config){
51097     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51098     if(this.split){
51099         this.split.placement = Roo.SplitBar.BOTTOM;
51100         this.split.orientation = Roo.SplitBar.VERTICAL;
51101         this.split.el.addClass("x-layout-split-v");
51102     }
51103     var size = config.initialSize || config.height;
51104     if(typeof size != "undefined"){
51105         this.el.setHeight(size);
51106     }
51107 };
51108 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51109     orientation: Roo.SplitBar.VERTICAL,
51110     getBox : function(){
51111         if(this.collapsed){
51112             return this.collapsedEl.getBox();
51113         }
51114         var box = this.el.getBox();
51115         if(this.split){
51116             var sh = this.split.el.getHeight();
51117             box.height += sh;
51118             box.y -= sh;
51119         }
51120         return box;
51121     },
51122     
51123     updateBox : function(box){
51124         if(this.split && !this.collapsed){
51125             var sh = this.split.el.getHeight();
51126             box.height -= sh;
51127             box.y += sh;
51128             this.split.el.setLeft(box.x);
51129             this.split.el.setTop(box.y-sh);
51130             this.split.el.setWidth(box.width);
51131         }
51132         if(this.collapsed){
51133             this.updateBody(box.width, null);
51134         }
51135         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51136     }
51137 });
51138
51139 Roo.EastLayoutRegion = function(mgr, config){
51140     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51141     if(this.split){
51142         this.split.placement = Roo.SplitBar.RIGHT;
51143         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51144         this.split.el.addClass("x-layout-split-h");
51145     }
51146     var size = config.initialSize || config.width;
51147     if(typeof size != "undefined"){
51148         this.el.setWidth(size);
51149     }
51150 };
51151 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51152     orientation: Roo.SplitBar.HORIZONTAL,
51153     getBox : function(){
51154         if(this.collapsed){
51155             return this.collapsedEl.getBox();
51156         }
51157         var box = this.el.getBox();
51158         if(this.split){
51159             var sw = this.split.el.getWidth();
51160             box.width += sw;
51161             box.x -= sw;
51162         }
51163         return box;
51164     },
51165
51166     updateBox : function(box){
51167         if(this.split && !this.collapsed){
51168             var sw = this.split.el.getWidth();
51169             box.width -= sw;
51170             this.split.el.setLeft(box.x);
51171             this.split.el.setTop(box.y);
51172             this.split.el.setHeight(box.height);
51173             box.x += sw;
51174         }
51175         if(this.collapsed){
51176             this.updateBody(null, box.height);
51177         }
51178         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51179     }
51180 });
51181
51182 Roo.WestLayoutRegion = function(mgr, config){
51183     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51184     if(this.split){
51185         this.split.placement = Roo.SplitBar.LEFT;
51186         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51187         this.split.el.addClass("x-layout-split-h");
51188     }
51189     var size = config.initialSize || config.width;
51190     if(typeof size != "undefined"){
51191         this.el.setWidth(size);
51192     }
51193 };
51194 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51195     orientation: Roo.SplitBar.HORIZONTAL,
51196     getBox : function(){
51197         if(this.collapsed){
51198             return this.collapsedEl.getBox();
51199         }
51200         var box = this.el.getBox();
51201         if(this.split){
51202             box.width += this.split.el.getWidth();
51203         }
51204         return box;
51205     },
51206     
51207     updateBox : function(box){
51208         if(this.split && !this.collapsed){
51209             var sw = this.split.el.getWidth();
51210             box.width -= sw;
51211             this.split.el.setLeft(box.x+box.width);
51212             this.split.el.setTop(box.y);
51213             this.split.el.setHeight(box.height);
51214         }
51215         if(this.collapsed){
51216             this.updateBody(null, box.height);
51217         }
51218         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51219     }
51220 });
51221 /*
51222  * Based on:
51223  * Ext JS Library 1.1.1
51224  * Copyright(c) 2006-2007, Ext JS, LLC.
51225  *
51226  * Originally Released Under LGPL - original licence link has changed is not relivant.
51227  *
51228  * Fork - LGPL
51229  * <script type="text/javascript">
51230  */
51231  
51232  
51233 /*
51234  * Private internal class for reading and applying state
51235  */
51236 Roo.LayoutStateManager = function(layout){
51237      // default empty state
51238      this.state = {
51239         north: {},
51240         south: {},
51241         east: {},
51242         west: {}       
51243     };
51244 };
51245
51246 Roo.LayoutStateManager.prototype = {
51247     init : function(layout, provider){
51248         this.provider = provider;
51249         var state = provider.get(layout.id+"-layout-state");
51250         if(state){
51251             var wasUpdating = layout.isUpdating();
51252             if(!wasUpdating){
51253                 layout.beginUpdate();
51254             }
51255             for(var key in state){
51256                 if(typeof state[key] != "function"){
51257                     var rstate = state[key];
51258                     var r = layout.getRegion(key);
51259                     if(r && rstate){
51260                         if(rstate.size){
51261                             r.resizeTo(rstate.size);
51262                         }
51263                         if(rstate.collapsed == true){
51264                             r.collapse(true);
51265                         }else{
51266                             r.expand(null, true);
51267                         }
51268                     }
51269                 }
51270             }
51271             if(!wasUpdating){
51272                 layout.endUpdate();
51273             }
51274             this.state = state; 
51275         }
51276         this.layout = layout;
51277         layout.on("regionresized", this.onRegionResized, this);
51278         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51279         layout.on("regionexpanded", this.onRegionExpanded, this);
51280     },
51281     
51282     storeState : function(){
51283         this.provider.set(this.layout.id+"-layout-state", this.state);
51284     },
51285     
51286     onRegionResized : function(region, newSize){
51287         this.state[region.getPosition()].size = newSize;
51288         this.storeState();
51289     },
51290     
51291     onRegionCollapsed : function(region){
51292         this.state[region.getPosition()].collapsed = true;
51293         this.storeState();
51294     },
51295     
51296     onRegionExpanded : function(region){
51297         this.state[region.getPosition()].collapsed = false;
51298         this.storeState();
51299     }
51300 };/*
51301  * Based on:
51302  * Ext JS Library 1.1.1
51303  * Copyright(c) 2006-2007, Ext JS, LLC.
51304  *
51305  * Originally Released Under LGPL - original licence link has changed is not relivant.
51306  *
51307  * Fork - LGPL
51308  * <script type="text/javascript">
51309  */
51310 /**
51311  * @class Roo.ContentPanel
51312  * @extends Roo.util.Observable
51313  * A basic ContentPanel element.
51314  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51315  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51316  * @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
51317  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51318  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51319  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51320  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51321  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51322  * @cfg {String} title          The title for this panel
51323  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51324  * @cfg {String} url            Calls {@link #setUrl} with this value
51325  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51326  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51327  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51328  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51329
51330  * @constructor
51331  * Create a new ContentPanel.
51332  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51333  * @param {String/Object} config A string to set only the title or a config object
51334  * @param {String} content (optional) Set the HTML content for this panel
51335  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51336  */
51337 Roo.ContentPanel = function(el, config, content){
51338     
51339      
51340     /*
51341     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51342         config = el;
51343         el = Roo.id();
51344     }
51345     if (config && config.parentLayout) { 
51346         el = config.parentLayout.el.createChild(); 
51347     }
51348     */
51349     if(el.autoCreate){ // xtype is available if this is called from factory
51350         config = el;
51351         el = Roo.id();
51352     }
51353     this.el = Roo.get(el);
51354     if(!this.el && config && config.autoCreate){
51355         if(typeof config.autoCreate == "object"){
51356             if(!config.autoCreate.id){
51357                 config.autoCreate.id = config.id||el;
51358             }
51359             this.el = Roo.DomHelper.append(document.body,
51360                         config.autoCreate, true);
51361         }else{
51362             this.el = Roo.DomHelper.append(document.body,
51363                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51364         }
51365     }
51366     this.closable = false;
51367     this.loaded = false;
51368     this.active = false;
51369     if(typeof config == "string"){
51370         this.title = config;
51371     }else{
51372         Roo.apply(this, config);
51373     }
51374     
51375     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51376         this.wrapEl = this.el.wrap();
51377         this.toolbar.container = this.el.insertSibling(false, 'before');
51378         this.toolbar = new Roo.Toolbar(this.toolbar);
51379     }
51380     
51381     // xtype created footer. - not sure if will work as we normally have to render first..
51382     if (this.footer && !this.footer.el && this.footer.xtype) {
51383         if (!this.wrapEl) {
51384             this.wrapEl = this.el.wrap();
51385         }
51386     
51387         this.footer.container = this.wrapEl.createChild();
51388          
51389         this.footer = Roo.factory(this.footer, Roo);
51390         
51391     }
51392     
51393     if(this.resizeEl){
51394         this.resizeEl = Roo.get(this.resizeEl, true);
51395     }else{
51396         this.resizeEl = this.el;
51397     }
51398     // handle view.xtype
51399     
51400  
51401     
51402     
51403     this.addEvents({
51404         /**
51405          * @event activate
51406          * Fires when this panel is activated. 
51407          * @param {Roo.ContentPanel} this
51408          */
51409         "activate" : true,
51410         /**
51411          * @event deactivate
51412          * Fires when this panel is activated. 
51413          * @param {Roo.ContentPanel} this
51414          */
51415         "deactivate" : true,
51416
51417         /**
51418          * @event resize
51419          * Fires when this panel is resized if fitToFrame is true.
51420          * @param {Roo.ContentPanel} this
51421          * @param {Number} width The width after any component adjustments
51422          * @param {Number} height The height after any component adjustments
51423          */
51424         "resize" : true,
51425         
51426          /**
51427          * @event render
51428          * Fires when this tab is created
51429          * @param {Roo.ContentPanel} this
51430          */
51431         "render" : true
51432         
51433         
51434         
51435     });
51436     
51437
51438     
51439     
51440     if(this.autoScroll){
51441         this.resizeEl.setStyle("overflow", "auto");
51442     } else {
51443         // fix randome scrolling
51444         this.el.on('scroll', function() {
51445             Roo.log('fix random scolling');
51446             this.scrollTo('top',0); 
51447         });
51448     }
51449     content = content || this.content;
51450     if(content){
51451         this.setContent(content);
51452     }
51453     if(config && config.url){
51454         this.setUrl(this.url, this.params, this.loadOnce);
51455     }
51456     
51457     
51458     
51459     Roo.ContentPanel.superclass.constructor.call(this);
51460     
51461     if (this.view && typeof(this.view.xtype) != 'undefined') {
51462         this.view.el = this.el.appendChild(document.createElement("div"));
51463         this.view = Roo.factory(this.view); 
51464         this.view.render  &&  this.view.render(false, '');  
51465     }
51466     
51467     
51468     this.fireEvent('render', this);
51469 };
51470
51471 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51472     tabTip:'',
51473     setRegion : function(region){
51474         this.region = region;
51475         if(region){
51476            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51477         }else{
51478            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51479         } 
51480     },
51481     
51482     /**
51483      * Returns the toolbar for this Panel if one was configured. 
51484      * @return {Roo.Toolbar} 
51485      */
51486     getToolbar : function(){
51487         return this.toolbar;
51488     },
51489     
51490     setActiveState : function(active){
51491         this.active = active;
51492         if(!active){
51493             this.fireEvent("deactivate", this);
51494         }else{
51495             this.fireEvent("activate", this);
51496         }
51497     },
51498     /**
51499      * Updates this panel's element
51500      * @param {String} content The new content
51501      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51502     */
51503     setContent : function(content, loadScripts){
51504         this.el.update(content, loadScripts);
51505     },
51506
51507     ignoreResize : function(w, h){
51508         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51509             return true;
51510         }else{
51511             this.lastSize = {width: w, height: h};
51512             return false;
51513         }
51514     },
51515     /**
51516      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51517      * @return {Roo.UpdateManager} The UpdateManager
51518      */
51519     getUpdateManager : function(){
51520         return this.el.getUpdateManager();
51521     },
51522      /**
51523      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51524      * @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:
51525 <pre><code>
51526 panel.load({
51527     url: "your-url.php",
51528     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51529     callback: yourFunction,
51530     scope: yourObject, //(optional scope)
51531     discardUrl: false,
51532     nocache: false,
51533     text: "Loading...",
51534     timeout: 30,
51535     scripts: false
51536 });
51537 </code></pre>
51538      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51539      * 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.
51540      * @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}
51541      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51542      * @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.
51543      * @return {Roo.ContentPanel} this
51544      */
51545     load : function(){
51546         var um = this.el.getUpdateManager();
51547         um.update.apply(um, arguments);
51548         return this;
51549     },
51550
51551
51552     /**
51553      * 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.
51554      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51555      * @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)
51556      * @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)
51557      * @return {Roo.UpdateManager} The UpdateManager
51558      */
51559     setUrl : function(url, params, loadOnce){
51560         if(this.refreshDelegate){
51561             this.removeListener("activate", this.refreshDelegate);
51562         }
51563         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51564         this.on("activate", this.refreshDelegate);
51565         return this.el.getUpdateManager();
51566     },
51567     
51568     _handleRefresh : function(url, params, loadOnce){
51569         if(!loadOnce || !this.loaded){
51570             var updater = this.el.getUpdateManager();
51571             updater.update(url, params, this._setLoaded.createDelegate(this));
51572         }
51573     },
51574     
51575     _setLoaded : function(){
51576         this.loaded = true;
51577     }, 
51578     
51579     /**
51580      * Returns this panel's id
51581      * @return {String} 
51582      */
51583     getId : function(){
51584         return this.el.id;
51585     },
51586     
51587     /** 
51588      * Returns this panel's element - used by regiosn to add.
51589      * @return {Roo.Element} 
51590      */
51591     getEl : function(){
51592         return this.wrapEl || this.el;
51593     },
51594     
51595     adjustForComponents : function(width, height)
51596     {
51597         //Roo.log('adjustForComponents ');
51598         if(this.resizeEl != this.el){
51599             width -= this.el.getFrameWidth('lr');
51600             height -= this.el.getFrameWidth('tb');
51601         }
51602         if(this.toolbar){
51603             var te = this.toolbar.getEl();
51604             height -= te.getHeight();
51605             te.setWidth(width);
51606         }
51607         if(this.footer){
51608             var te = this.footer.getEl();
51609             Roo.log("footer:" + te.getHeight());
51610             
51611             height -= te.getHeight();
51612             te.setWidth(width);
51613         }
51614         
51615         
51616         if(this.adjustments){
51617             width += this.adjustments[0];
51618             height += this.adjustments[1];
51619         }
51620         return {"width": width, "height": height};
51621     },
51622     
51623     setSize : function(width, height){
51624         if(this.fitToFrame && !this.ignoreResize(width, height)){
51625             if(this.fitContainer && this.resizeEl != this.el){
51626                 this.el.setSize(width, height);
51627             }
51628             var size = this.adjustForComponents(width, height);
51629             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51630             this.fireEvent('resize', this, size.width, size.height);
51631         }
51632     },
51633     
51634     /**
51635      * Returns this panel's title
51636      * @return {String} 
51637      */
51638     getTitle : function(){
51639         return this.title;
51640     },
51641     
51642     /**
51643      * Set this panel's title
51644      * @param {String} title
51645      */
51646     setTitle : function(title){
51647         this.title = title;
51648         if(this.region){
51649             this.region.updatePanelTitle(this, title);
51650         }
51651     },
51652     
51653     /**
51654      * Returns true is this panel was configured to be closable
51655      * @return {Boolean} 
51656      */
51657     isClosable : function(){
51658         return this.closable;
51659     },
51660     
51661     beforeSlide : function(){
51662         this.el.clip();
51663         this.resizeEl.clip();
51664     },
51665     
51666     afterSlide : function(){
51667         this.el.unclip();
51668         this.resizeEl.unclip();
51669     },
51670     
51671     /**
51672      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51673      *   Will fail silently if the {@link #setUrl} method has not been called.
51674      *   This does not activate the panel, just updates its content.
51675      */
51676     refresh : function(){
51677         if(this.refreshDelegate){
51678            this.loaded = false;
51679            this.refreshDelegate();
51680         }
51681     },
51682     
51683     /**
51684      * Destroys this panel
51685      */
51686     destroy : function(){
51687         this.el.removeAllListeners();
51688         var tempEl = document.createElement("span");
51689         tempEl.appendChild(this.el.dom);
51690         tempEl.innerHTML = "";
51691         this.el.remove();
51692         this.el = null;
51693     },
51694     
51695     /**
51696      * form - if the content panel contains a form - this is a reference to it.
51697      * @type {Roo.form.Form}
51698      */
51699     form : false,
51700     /**
51701      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51702      *    This contains a reference to it.
51703      * @type {Roo.View}
51704      */
51705     view : false,
51706     
51707       /**
51708      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51709      * <pre><code>
51710
51711 layout.addxtype({
51712        xtype : 'Form',
51713        items: [ .... ]
51714    }
51715 );
51716
51717 </code></pre>
51718      * @param {Object} cfg Xtype definition of item to add.
51719      */
51720     
51721     addxtype : function(cfg) {
51722         // add form..
51723         if (cfg.xtype.match(/^Form$/)) {
51724             
51725             var el;
51726             //if (this.footer) {
51727             //    el = this.footer.container.insertSibling(false, 'before');
51728             //} else {
51729                 el = this.el.createChild();
51730             //}
51731
51732             this.form = new  Roo.form.Form(cfg);
51733             
51734             
51735             if ( this.form.allItems.length) this.form.render(el.dom);
51736             return this.form;
51737         }
51738         // should only have one of theses..
51739         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51740             // views.. should not be just added - used named prop 'view''
51741             
51742             cfg.el = this.el.appendChild(document.createElement("div"));
51743             // factory?
51744             
51745             var ret = new Roo.factory(cfg);
51746              
51747              ret.render && ret.render(false, ''); // render blank..
51748             this.view = ret;
51749             return ret;
51750         }
51751         return false;
51752     }
51753 });
51754
51755 /**
51756  * @class Roo.GridPanel
51757  * @extends Roo.ContentPanel
51758  * @constructor
51759  * Create a new GridPanel.
51760  * @param {Roo.grid.Grid} grid The grid for this panel
51761  * @param {String/Object} config A string to set only the panel's title, or a config object
51762  */
51763 Roo.GridPanel = function(grid, config){
51764     
51765   
51766     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51767         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51768         
51769     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51770     
51771     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51772     
51773     if(this.toolbar){
51774         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51775     }
51776     // xtype created footer. - not sure if will work as we normally have to render first..
51777     if (this.footer && !this.footer.el && this.footer.xtype) {
51778         
51779         this.footer.container = this.grid.getView().getFooterPanel(true);
51780         this.footer.dataSource = this.grid.dataSource;
51781         this.footer = Roo.factory(this.footer, Roo);
51782         
51783     }
51784     
51785     grid.monitorWindowResize = false; // turn off autosizing
51786     grid.autoHeight = false;
51787     grid.autoWidth = false;
51788     this.grid = grid;
51789     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51790 };
51791
51792 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51793     getId : function(){
51794         return this.grid.id;
51795     },
51796     
51797     /**
51798      * Returns the grid for this panel
51799      * @return {Roo.grid.Grid} 
51800      */
51801     getGrid : function(){
51802         return this.grid;    
51803     },
51804     
51805     setSize : function(width, height){
51806         if(!this.ignoreResize(width, height)){
51807             var grid = this.grid;
51808             var size = this.adjustForComponents(width, height);
51809             grid.getGridEl().setSize(size.width, size.height);
51810             grid.autoSize();
51811         }
51812     },
51813     
51814     beforeSlide : function(){
51815         this.grid.getView().scroller.clip();
51816     },
51817     
51818     afterSlide : function(){
51819         this.grid.getView().scroller.unclip();
51820     },
51821     
51822     destroy : function(){
51823         this.grid.destroy();
51824         delete this.grid;
51825         Roo.GridPanel.superclass.destroy.call(this); 
51826     }
51827 });
51828
51829
51830 /**
51831  * @class Roo.NestedLayoutPanel
51832  * @extends Roo.ContentPanel
51833  * @constructor
51834  * Create a new NestedLayoutPanel.
51835  * 
51836  * 
51837  * @param {Roo.BorderLayout} layout The layout for this panel
51838  * @param {String/Object} config A string to set only the title or a config object
51839  */
51840 Roo.NestedLayoutPanel = function(layout, config)
51841 {
51842     // construct with only one argument..
51843     /* FIXME - implement nicer consturctors
51844     if (layout.layout) {
51845         config = layout;
51846         layout = config.layout;
51847         delete config.layout;
51848     }
51849     if (layout.xtype && !layout.getEl) {
51850         // then layout needs constructing..
51851         layout = Roo.factory(layout, Roo);
51852     }
51853     */
51854     
51855     
51856     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51857     
51858     layout.monitorWindowResize = false; // turn off autosizing
51859     this.layout = layout;
51860     this.layout.getEl().addClass("x-layout-nested-layout");
51861     
51862     
51863     
51864     
51865 };
51866
51867 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51868
51869     setSize : function(width, height){
51870         if(!this.ignoreResize(width, height)){
51871             var size = this.adjustForComponents(width, height);
51872             var el = this.layout.getEl();
51873             el.setSize(size.width, size.height);
51874             var touch = el.dom.offsetWidth;
51875             this.layout.layout();
51876             // ie requires a double layout on the first pass
51877             if(Roo.isIE && !this.initialized){
51878                 this.initialized = true;
51879                 this.layout.layout();
51880             }
51881         }
51882     },
51883     
51884     // activate all subpanels if not currently active..
51885     
51886     setActiveState : function(active){
51887         this.active = active;
51888         if(!active){
51889             this.fireEvent("deactivate", this);
51890             return;
51891         }
51892         
51893         this.fireEvent("activate", this);
51894         // not sure if this should happen before or after..
51895         if (!this.layout) {
51896             return; // should not happen..
51897         }
51898         var reg = false;
51899         for (var r in this.layout.regions) {
51900             reg = this.layout.getRegion(r);
51901             if (reg.getActivePanel()) {
51902                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51903                 reg.setActivePanel(reg.getActivePanel());
51904                 continue;
51905             }
51906             if (!reg.panels.length) {
51907                 continue;
51908             }
51909             reg.showPanel(reg.getPanel(0));
51910         }
51911         
51912         
51913         
51914         
51915     },
51916     
51917     /**
51918      * Returns the nested BorderLayout for this panel
51919      * @return {Roo.BorderLayout} 
51920      */
51921     getLayout : function(){
51922         return this.layout;
51923     },
51924     
51925      /**
51926      * Adds a xtype elements to the layout of the nested panel
51927      * <pre><code>
51928
51929 panel.addxtype({
51930        xtype : 'ContentPanel',
51931        region: 'west',
51932        items: [ .... ]
51933    }
51934 );
51935
51936 panel.addxtype({
51937         xtype : 'NestedLayoutPanel',
51938         region: 'west',
51939         layout: {
51940            center: { },
51941            west: { }   
51942         },
51943         items : [ ... list of content panels or nested layout panels.. ]
51944    }
51945 );
51946 </code></pre>
51947      * @param {Object} cfg Xtype definition of item to add.
51948      */
51949     addxtype : function(cfg) {
51950         return this.layout.addxtype(cfg);
51951     
51952     }
51953 });
51954
51955 Roo.ScrollPanel = function(el, config, content){
51956     config = config || {};
51957     config.fitToFrame = true;
51958     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51959     
51960     this.el.dom.style.overflow = "hidden";
51961     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51962     this.el.removeClass("x-layout-inactive-content");
51963     this.el.on("mousewheel", this.onWheel, this);
51964
51965     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51966     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51967     up.unselectable(); down.unselectable();
51968     up.on("click", this.scrollUp, this);
51969     down.on("click", this.scrollDown, this);
51970     up.addClassOnOver("x-scroller-btn-over");
51971     down.addClassOnOver("x-scroller-btn-over");
51972     up.addClassOnClick("x-scroller-btn-click");
51973     down.addClassOnClick("x-scroller-btn-click");
51974     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51975
51976     this.resizeEl = this.el;
51977     this.el = wrap; this.up = up; this.down = down;
51978 };
51979
51980 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51981     increment : 100,
51982     wheelIncrement : 5,
51983     scrollUp : function(){
51984         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51985     },
51986
51987     scrollDown : function(){
51988         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51989     },
51990
51991     afterScroll : function(){
51992         var el = this.resizeEl;
51993         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51994         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51995         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51996     },
51997
51998     setSize : function(){
51999         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52000         this.afterScroll();
52001     },
52002
52003     onWheel : function(e){
52004         var d = e.getWheelDelta();
52005         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52006         this.afterScroll();
52007         e.stopEvent();
52008     },
52009
52010     setContent : function(content, loadScripts){
52011         this.resizeEl.update(content, loadScripts);
52012     }
52013
52014 });
52015
52016
52017
52018
52019
52020
52021
52022
52023
52024 /**
52025  * @class Roo.TreePanel
52026  * @extends Roo.ContentPanel
52027  * @constructor
52028  * Create a new TreePanel. - defaults to fit/scoll contents.
52029  * @param {String/Object} config A string to set only the panel's title, or a config object
52030  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52031  */
52032 Roo.TreePanel = function(config){
52033     var el = config.el;
52034     var tree = config.tree;
52035     delete config.tree; 
52036     delete config.el; // hopefull!
52037     
52038     // wrapper for IE7 strict & safari scroll issue
52039     
52040     var treeEl = el.createChild();
52041     config.resizeEl = treeEl;
52042     
52043     
52044     
52045     Roo.TreePanel.superclass.constructor.call(this, el, config);
52046  
52047  
52048     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52049     //console.log(tree);
52050     this.on('activate', function()
52051     {
52052         if (this.tree.rendered) {
52053             return;
52054         }
52055         //console.log('render tree');
52056         this.tree.render();
52057     });
52058     // this should not be needed.. - it's actually the 'el' that resizes?
52059     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52060     
52061     //this.on('resize',  function (cp, w, h) {
52062     //        this.tree.innerCt.setWidth(w);
52063     //        this.tree.innerCt.setHeight(h);
52064     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52065     //});
52066
52067         
52068     
52069 };
52070
52071 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52072     fitToFrame : true,
52073     autoScroll : true
52074 });
52075
52076
52077
52078
52079
52080
52081
52082
52083
52084
52085
52086 /*
52087  * Based on:
52088  * Ext JS Library 1.1.1
52089  * Copyright(c) 2006-2007, Ext JS, LLC.
52090  *
52091  * Originally Released Under LGPL - original licence link has changed is not relivant.
52092  *
52093  * Fork - LGPL
52094  * <script type="text/javascript">
52095  */
52096  
52097
52098 /**
52099  * @class Roo.ReaderLayout
52100  * @extends Roo.BorderLayout
52101  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52102  * center region containing two nested regions (a top one for a list view and one for item preview below),
52103  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52104  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52105  * expedites the setup of the overall layout and regions for this common application style.
52106  * Example:
52107  <pre><code>
52108 var reader = new Roo.ReaderLayout();
52109 var CP = Roo.ContentPanel;  // shortcut for adding
52110
52111 reader.beginUpdate();
52112 reader.add("north", new CP("north", "North"));
52113 reader.add("west", new CP("west", {title: "West"}));
52114 reader.add("east", new CP("east", {title: "East"}));
52115
52116 reader.regions.listView.add(new CP("listView", "List"));
52117 reader.regions.preview.add(new CP("preview", "Preview"));
52118 reader.endUpdate();
52119 </code></pre>
52120 * @constructor
52121 * Create a new ReaderLayout
52122 * @param {Object} config Configuration options
52123 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52124 * document.body if omitted)
52125 */
52126 Roo.ReaderLayout = function(config, renderTo){
52127     var c = config || {size:{}};
52128     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52129         north: c.north !== false ? Roo.apply({
52130             split:false,
52131             initialSize: 32,
52132             titlebar: false
52133         }, c.north) : false,
52134         west: c.west !== false ? Roo.apply({
52135             split:true,
52136             initialSize: 200,
52137             minSize: 175,
52138             maxSize: 400,
52139             titlebar: true,
52140             collapsible: true,
52141             animate: true,
52142             margins:{left:5,right:0,bottom:5,top:5},
52143             cmargins:{left:5,right:5,bottom:5,top:5}
52144         }, c.west) : false,
52145         east: c.east !== false ? Roo.apply({
52146             split:true,
52147             initialSize: 200,
52148             minSize: 175,
52149             maxSize: 400,
52150             titlebar: true,
52151             collapsible: true,
52152             animate: true,
52153             margins:{left:0,right:5,bottom:5,top:5},
52154             cmargins:{left:5,right:5,bottom:5,top:5}
52155         }, c.east) : false,
52156         center: Roo.apply({
52157             tabPosition: 'top',
52158             autoScroll:false,
52159             closeOnTab: true,
52160             titlebar:false,
52161             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52162         }, c.center)
52163     });
52164
52165     this.el.addClass('x-reader');
52166
52167     this.beginUpdate();
52168
52169     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52170         south: c.preview !== false ? Roo.apply({
52171             split:true,
52172             initialSize: 200,
52173             minSize: 100,
52174             autoScroll:true,
52175             collapsible:true,
52176             titlebar: true,
52177             cmargins:{top:5,left:0, right:0, bottom:0}
52178         }, c.preview) : false,
52179         center: Roo.apply({
52180             autoScroll:false,
52181             titlebar:false,
52182             minHeight:200
52183         }, c.listView)
52184     });
52185     this.add('center', new Roo.NestedLayoutPanel(inner,
52186             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52187
52188     this.endUpdate();
52189
52190     this.regions.preview = inner.getRegion('south');
52191     this.regions.listView = inner.getRegion('center');
52192 };
52193
52194 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52195  * Based on:
52196  * Ext JS Library 1.1.1
52197  * Copyright(c) 2006-2007, Ext JS, LLC.
52198  *
52199  * Originally Released Under LGPL - original licence link has changed is not relivant.
52200  *
52201  * Fork - LGPL
52202  * <script type="text/javascript">
52203  */
52204  
52205 /**
52206  * @class Roo.grid.Grid
52207  * @extends Roo.util.Observable
52208  * This class represents the primary interface of a component based grid control.
52209  * <br><br>Usage:<pre><code>
52210  var grid = new Roo.grid.Grid("my-container-id", {
52211      ds: myDataStore,
52212      cm: myColModel,
52213      selModel: mySelectionModel,
52214      autoSizeColumns: true,
52215      monitorWindowResize: false,
52216      trackMouseOver: true
52217  });
52218  // set any options
52219  grid.render();
52220  * </code></pre>
52221  * <b>Common Problems:</b><br/>
52222  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52223  * element will correct this<br/>
52224  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52225  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52226  * are unpredictable.<br/>
52227  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52228  * grid to calculate dimensions/offsets.<br/>
52229   * @constructor
52230  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52231  * The container MUST have some type of size defined for the grid to fill. The container will be
52232  * automatically set to position relative if it isn't already.
52233  * @param {Object} config A config object that sets properties on this grid.
52234  */
52235 Roo.grid.Grid = function(container, config){
52236         // initialize the container
52237         this.container = Roo.get(container);
52238         this.container.update("");
52239         this.container.setStyle("overflow", "hidden");
52240     this.container.addClass('x-grid-container');
52241
52242     this.id = this.container.id;
52243
52244     Roo.apply(this, config);
52245     // check and correct shorthanded configs
52246     if(this.ds){
52247         this.dataSource = this.ds;
52248         delete this.ds;
52249     }
52250     if(this.cm){
52251         this.colModel = this.cm;
52252         delete this.cm;
52253     }
52254     if(this.sm){
52255         this.selModel = this.sm;
52256         delete this.sm;
52257     }
52258
52259     if (this.selModel) {
52260         this.selModel = Roo.factory(this.selModel, Roo.grid);
52261         this.sm = this.selModel;
52262         this.sm.xmodule = this.xmodule || false;
52263     }
52264     if (typeof(this.colModel.config) == 'undefined') {
52265         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52266         this.cm = this.colModel;
52267         this.cm.xmodule = this.xmodule || false;
52268     }
52269     if (this.dataSource) {
52270         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52271         this.ds = this.dataSource;
52272         this.ds.xmodule = this.xmodule || false;
52273          
52274     }
52275     
52276     
52277     
52278     if(this.width){
52279         this.container.setWidth(this.width);
52280     }
52281
52282     if(this.height){
52283         this.container.setHeight(this.height);
52284     }
52285     /** @private */
52286         this.addEvents({
52287         // raw events
52288         /**
52289          * @event click
52290          * The raw click event for the entire grid.
52291          * @param {Roo.EventObject} e
52292          */
52293         "click" : true,
52294         /**
52295          * @event dblclick
52296          * The raw dblclick event for the entire grid.
52297          * @param {Roo.EventObject} e
52298          */
52299         "dblclick" : true,
52300         /**
52301          * @event contextmenu
52302          * The raw contextmenu event for the entire grid.
52303          * @param {Roo.EventObject} e
52304          */
52305         "contextmenu" : true,
52306         /**
52307          * @event mousedown
52308          * The raw mousedown event for the entire grid.
52309          * @param {Roo.EventObject} e
52310          */
52311         "mousedown" : true,
52312         /**
52313          * @event mouseup
52314          * The raw mouseup event for the entire grid.
52315          * @param {Roo.EventObject} e
52316          */
52317         "mouseup" : true,
52318         /**
52319          * @event mouseover
52320          * The raw mouseover event for the entire grid.
52321          * @param {Roo.EventObject} e
52322          */
52323         "mouseover" : true,
52324         /**
52325          * @event mouseout
52326          * The raw mouseout event for the entire grid.
52327          * @param {Roo.EventObject} e
52328          */
52329         "mouseout" : true,
52330         /**
52331          * @event keypress
52332          * The raw keypress event for the entire grid.
52333          * @param {Roo.EventObject} e
52334          */
52335         "keypress" : true,
52336         /**
52337          * @event keydown
52338          * The raw keydown event for the entire grid.
52339          * @param {Roo.EventObject} e
52340          */
52341         "keydown" : true,
52342
52343         // custom events
52344
52345         /**
52346          * @event cellclick
52347          * Fires when a cell is clicked
52348          * @param {Grid} this
52349          * @param {Number} rowIndex
52350          * @param {Number} columnIndex
52351          * @param {Roo.EventObject} e
52352          */
52353         "cellclick" : true,
52354         /**
52355          * @event celldblclick
52356          * Fires when a cell is double clicked
52357          * @param {Grid} this
52358          * @param {Number} rowIndex
52359          * @param {Number} columnIndex
52360          * @param {Roo.EventObject} e
52361          */
52362         "celldblclick" : true,
52363         /**
52364          * @event rowclick
52365          * Fires when a row is clicked
52366          * @param {Grid} this
52367          * @param {Number} rowIndex
52368          * @param {Roo.EventObject} e
52369          */
52370         "rowclick" : true,
52371         /**
52372          * @event rowdblclick
52373          * Fires when a row is double clicked
52374          * @param {Grid} this
52375          * @param {Number} rowIndex
52376          * @param {Roo.EventObject} e
52377          */
52378         "rowdblclick" : true,
52379         /**
52380          * @event headerclick
52381          * Fires when a header is clicked
52382          * @param {Grid} this
52383          * @param {Number} columnIndex
52384          * @param {Roo.EventObject} e
52385          */
52386         "headerclick" : true,
52387         /**
52388          * @event headerdblclick
52389          * Fires when a header cell is double clicked
52390          * @param {Grid} this
52391          * @param {Number} columnIndex
52392          * @param {Roo.EventObject} e
52393          */
52394         "headerdblclick" : true,
52395         /**
52396          * @event rowcontextmenu
52397          * Fires when a row is right clicked
52398          * @param {Grid} this
52399          * @param {Number} rowIndex
52400          * @param {Roo.EventObject} e
52401          */
52402         "rowcontextmenu" : true,
52403         /**
52404          * @event cellcontextmenu
52405          * Fires when a cell is right clicked
52406          * @param {Grid} this
52407          * @param {Number} rowIndex
52408          * @param {Number} cellIndex
52409          * @param {Roo.EventObject} e
52410          */
52411          "cellcontextmenu" : true,
52412         /**
52413          * @event headercontextmenu
52414          * Fires when a header is right clicked
52415          * @param {Grid} this
52416          * @param {Number} columnIndex
52417          * @param {Roo.EventObject} e
52418          */
52419         "headercontextmenu" : true,
52420         /**
52421          * @event bodyscroll
52422          * Fires when the body element is scrolled
52423          * @param {Number} scrollLeft
52424          * @param {Number} scrollTop
52425          */
52426         "bodyscroll" : true,
52427         /**
52428          * @event columnresize
52429          * Fires when the user resizes a column
52430          * @param {Number} columnIndex
52431          * @param {Number} newSize
52432          */
52433         "columnresize" : true,
52434         /**
52435          * @event columnmove
52436          * Fires when the user moves a column
52437          * @param {Number} oldIndex
52438          * @param {Number} newIndex
52439          */
52440         "columnmove" : true,
52441         /**
52442          * @event startdrag
52443          * Fires when row(s) start being dragged
52444          * @param {Grid} this
52445          * @param {Roo.GridDD} dd The drag drop object
52446          * @param {event} e The raw browser event
52447          */
52448         "startdrag" : true,
52449         /**
52450          * @event enddrag
52451          * Fires when a drag operation is complete
52452          * @param {Grid} this
52453          * @param {Roo.GridDD} dd The drag drop object
52454          * @param {event} e The raw browser event
52455          */
52456         "enddrag" : true,
52457         /**
52458          * @event dragdrop
52459          * Fires when dragged row(s) are dropped on a valid DD target
52460          * @param {Grid} this
52461          * @param {Roo.GridDD} dd The drag drop object
52462          * @param {String} targetId The target drag drop object
52463          * @param {event} e The raw browser event
52464          */
52465         "dragdrop" : true,
52466         /**
52467          * @event dragover
52468          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52469          * @param {Grid} this
52470          * @param {Roo.GridDD} dd The drag drop object
52471          * @param {String} targetId The target drag drop object
52472          * @param {event} e The raw browser event
52473          */
52474         "dragover" : true,
52475         /**
52476          * @event dragenter
52477          *  Fires when the dragged row(s) first cross another DD target while being dragged
52478          * @param {Grid} this
52479          * @param {Roo.GridDD} dd The drag drop object
52480          * @param {String} targetId The target drag drop object
52481          * @param {event} e The raw browser event
52482          */
52483         "dragenter" : true,
52484         /**
52485          * @event dragout
52486          * Fires when the dragged row(s) leave another DD target while being dragged
52487          * @param {Grid} this
52488          * @param {Roo.GridDD} dd The drag drop object
52489          * @param {String} targetId The target drag drop object
52490          * @param {event} e The raw browser event
52491          */
52492         "dragout" : true,
52493         /**
52494          * @event rowclass
52495          * Fires when a row is rendered, so you can change add a style to it.
52496          * @param {GridView} gridview   The grid view
52497          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52498          */
52499         'rowclass' : true,
52500
52501         /**
52502          * @event render
52503          * Fires when the grid is rendered
52504          * @param {Grid} grid
52505          */
52506         'render' : true
52507     });
52508
52509     Roo.grid.Grid.superclass.constructor.call(this);
52510 };
52511 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52512     
52513     /**
52514      * @cfg {String} ddGroup - drag drop group.
52515      */
52516
52517     /**
52518      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52519      */
52520     minColumnWidth : 25,
52521
52522     /**
52523      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52524      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52525      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52526      */
52527     autoSizeColumns : false,
52528
52529     /**
52530      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52531      */
52532     autoSizeHeaders : true,
52533
52534     /**
52535      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52536      */
52537     monitorWindowResize : true,
52538
52539     /**
52540      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52541      * rows measured to get a columns size. Default is 0 (all rows).
52542      */
52543     maxRowsToMeasure : 0,
52544
52545     /**
52546      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52547      */
52548     trackMouseOver : true,
52549
52550     /**
52551     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52552     */
52553     
52554     /**
52555     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52556     */
52557     enableDragDrop : false,
52558     
52559     /**
52560     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52561     */
52562     enableColumnMove : true,
52563     
52564     /**
52565     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52566     */
52567     enableColumnHide : true,
52568     
52569     /**
52570     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52571     */
52572     enableRowHeightSync : false,
52573     
52574     /**
52575     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52576     */
52577     stripeRows : true,
52578     
52579     /**
52580     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52581     */
52582     autoHeight : false,
52583
52584     /**
52585      * @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.
52586      */
52587     autoExpandColumn : false,
52588
52589     /**
52590     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52591     * Default is 50.
52592     */
52593     autoExpandMin : 50,
52594
52595     /**
52596     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52597     */
52598     autoExpandMax : 1000,
52599
52600     /**
52601     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52602     */
52603     view : null,
52604
52605     /**
52606     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52607     */
52608     loadMask : false,
52609     /**
52610     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52611     */
52612     dropTarget: false,
52613     
52614    
52615     
52616     // private
52617     rendered : false,
52618
52619     /**
52620     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52621     * of a fixed width. Default is false.
52622     */
52623     /**
52624     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52625     */
52626     /**
52627      * Called once after all setup has been completed and the grid is ready to be rendered.
52628      * @return {Roo.grid.Grid} this
52629      */
52630     render : function()
52631     {
52632         var c = this.container;
52633         // try to detect autoHeight/width mode
52634         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52635             this.autoHeight = true;
52636         }
52637         var view = this.getView();
52638         view.init(this);
52639
52640         c.on("click", this.onClick, this);
52641         c.on("dblclick", this.onDblClick, this);
52642         c.on("contextmenu", this.onContextMenu, this);
52643         c.on("keydown", this.onKeyDown, this);
52644         if (Roo.isTouch) {
52645             c.on("touchstart", this.onTouchStart, this);
52646         }
52647
52648         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52649
52650         this.getSelectionModel().init(this);
52651
52652         view.render();
52653
52654         if(this.loadMask){
52655             this.loadMask = new Roo.LoadMask(this.container,
52656                     Roo.apply({store:this.dataSource}, this.loadMask));
52657         }
52658         
52659         
52660         if (this.toolbar && this.toolbar.xtype) {
52661             this.toolbar.container = this.getView().getHeaderPanel(true);
52662             this.toolbar = new Roo.Toolbar(this.toolbar);
52663         }
52664         if (this.footer && this.footer.xtype) {
52665             this.footer.dataSource = this.getDataSource();
52666             this.footer.container = this.getView().getFooterPanel(true);
52667             this.footer = Roo.factory(this.footer, Roo);
52668         }
52669         if (this.dropTarget && this.dropTarget.xtype) {
52670             delete this.dropTarget.xtype;
52671             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52672         }
52673         
52674         
52675         this.rendered = true;
52676         this.fireEvent('render', this);
52677         return this;
52678     },
52679
52680         /**
52681          * Reconfigures the grid to use a different Store and Column Model.
52682          * The View will be bound to the new objects and refreshed.
52683          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52684          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52685          */
52686     reconfigure : function(dataSource, colModel){
52687         if(this.loadMask){
52688             this.loadMask.destroy();
52689             this.loadMask = new Roo.LoadMask(this.container,
52690                     Roo.apply({store:dataSource}, this.loadMask));
52691         }
52692         this.view.bind(dataSource, colModel);
52693         this.dataSource = dataSource;
52694         this.colModel = colModel;
52695         this.view.refresh(true);
52696     },
52697
52698     // private
52699     onKeyDown : function(e){
52700         this.fireEvent("keydown", e);
52701     },
52702
52703     /**
52704      * Destroy this grid.
52705      * @param {Boolean} removeEl True to remove the element
52706      */
52707     destroy : function(removeEl, keepListeners){
52708         if(this.loadMask){
52709             this.loadMask.destroy();
52710         }
52711         var c = this.container;
52712         c.removeAllListeners();
52713         this.view.destroy();
52714         this.colModel.purgeListeners();
52715         if(!keepListeners){
52716             this.purgeListeners();
52717         }
52718         c.update("");
52719         if(removeEl === true){
52720             c.remove();
52721         }
52722     },
52723
52724     // private
52725     processEvent : function(name, e){
52726         // does this fire select???
52727         //Roo.log('grid:processEvent '  + name);
52728         
52729         if (name != 'touchstart' ) {
52730             this.fireEvent(name, e);    
52731         }
52732         
52733         var t = e.getTarget();
52734         var v = this.view;
52735         var header = v.findHeaderIndex(t);
52736         if(header !== false){
52737             var ename = name == 'touchstart' ? 'click' : name;
52738              
52739             this.fireEvent("header" + ename, this, header, e);
52740         }else{
52741             var row = v.findRowIndex(t);
52742             var cell = v.findCellIndex(t);
52743             if (name == 'touchstart') {
52744                 // first touch is always a click.
52745                 // hopefull this happens after selection is updated.?
52746                 name = false;
52747                 
52748                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52749                     var cs = this.selModel.getSelectedCell();
52750                     if (row == cs[0] && cell == cs[1]){
52751                         name = 'dblclick';
52752                     }
52753                 }
52754                 if (typeof(this.selModel.getSelections) != 'undefined') {
52755                     var cs = this.selModel.getSelections();
52756                     var ds = this.dataSource;
52757                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52758                         name = 'dblclick';
52759                     }
52760                 }
52761                 if (!name) {
52762                     return;
52763                 }
52764             }
52765             
52766             
52767             if(row !== false){
52768                 this.fireEvent("row" + name, this, row, e);
52769                 if(cell !== false){
52770                     this.fireEvent("cell" + name, this, row, cell, e);
52771                 }
52772             }
52773         }
52774     },
52775
52776     // private
52777     onClick : function(e){
52778         this.processEvent("click", e);
52779     },
52780    // private
52781     onTouchStart : function(e){
52782         this.processEvent("touchstart", e);
52783     },
52784
52785     // private
52786     onContextMenu : function(e, t){
52787         this.processEvent("contextmenu", e);
52788     },
52789
52790     // private
52791     onDblClick : function(e){
52792         this.processEvent("dblclick", e);
52793     },
52794
52795     // private
52796     walkCells : function(row, col, step, fn, scope){
52797         var cm = this.colModel, clen = cm.getColumnCount();
52798         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52799         if(step < 0){
52800             if(col < 0){
52801                 row--;
52802                 first = false;
52803             }
52804             while(row >= 0){
52805                 if(!first){
52806                     col = clen-1;
52807                 }
52808                 first = false;
52809                 while(col >= 0){
52810                     if(fn.call(scope || this, row, col, cm) === true){
52811                         return [row, col];
52812                     }
52813                     col--;
52814                 }
52815                 row--;
52816             }
52817         } else {
52818             if(col >= clen){
52819                 row++;
52820                 first = false;
52821             }
52822             while(row < rlen){
52823                 if(!first){
52824                     col = 0;
52825                 }
52826                 first = false;
52827                 while(col < clen){
52828                     if(fn.call(scope || this, row, col, cm) === true){
52829                         return [row, col];
52830                     }
52831                     col++;
52832                 }
52833                 row++;
52834             }
52835         }
52836         return null;
52837     },
52838
52839     // private
52840     getSelections : function(){
52841         return this.selModel.getSelections();
52842     },
52843
52844     /**
52845      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52846      * but if manual update is required this method will initiate it.
52847      */
52848     autoSize : function(){
52849         if(this.rendered){
52850             this.view.layout();
52851             if(this.view.adjustForScroll){
52852                 this.view.adjustForScroll();
52853             }
52854         }
52855     },
52856
52857     /**
52858      * Returns the grid's underlying element.
52859      * @return {Element} The element
52860      */
52861     getGridEl : function(){
52862         return this.container;
52863     },
52864
52865     // private for compatibility, overridden by editor grid
52866     stopEditing : function(){},
52867
52868     /**
52869      * Returns the grid's SelectionModel.
52870      * @return {SelectionModel}
52871      */
52872     getSelectionModel : function(){
52873         if(!this.selModel){
52874             this.selModel = new Roo.grid.RowSelectionModel();
52875         }
52876         return this.selModel;
52877     },
52878
52879     /**
52880      * Returns the grid's DataSource.
52881      * @return {DataSource}
52882      */
52883     getDataSource : function(){
52884         return this.dataSource;
52885     },
52886
52887     /**
52888      * Returns the grid's ColumnModel.
52889      * @return {ColumnModel}
52890      */
52891     getColumnModel : function(){
52892         return this.colModel;
52893     },
52894
52895     /**
52896      * Returns the grid's GridView object.
52897      * @return {GridView}
52898      */
52899     getView : function(){
52900         if(!this.view){
52901             this.view = new Roo.grid.GridView(this.viewConfig);
52902         }
52903         return this.view;
52904     },
52905     /**
52906      * Called to get grid's drag proxy text, by default returns this.ddText.
52907      * @return {String}
52908      */
52909     getDragDropText : function(){
52910         var count = this.selModel.getCount();
52911         return String.format(this.ddText, count, count == 1 ? '' : 's');
52912     }
52913 });
52914 /**
52915  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52916  * %0 is replaced with the number of selected rows.
52917  * @type String
52918  */
52919 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52920  * Based on:
52921  * Ext JS Library 1.1.1
52922  * Copyright(c) 2006-2007, Ext JS, LLC.
52923  *
52924  * Originally Released Under LGPL - original licence link has changed is not relivant.
52925  *
52926  * Fork - LGPL
52927  * <script type="text/javascript">
52928  */
52929  
52930 Roo.grid.AbstractGridView = function(){
52931         this.grid = null;
52932         
52933         this.events = {
52934             "beforerowremoved" : true,
52935             "beforerowsinserted" : true,
52936             "beforerefresh" : true,
52937             "rowremoved" : true,
52938             "rowsinserted" : true,
52939             "rowupdated" : true,
52940             "refresh" : true
52941         };
52942     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52943 };
52944
52945 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52946     rowClass : "x-grid-row",
52947     cellClass : "x-grid-cell",
52948     tdClass : "x-grid-td",
52949     hdClass : "x-grid-hd",
52950     splitClass : "x-grid-hd-split",
52951     
52952     init: function(grid){
52953         this.grid = grid;
52954                 var cid = this.grid.getGridEl().id;
52955         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52956         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52957         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52958         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52959         },
52960         
52961     getColumnRenderers : function(){
52962         var renderers = [];
52963         var cm = this.grid.colModel;
52964         var colCount = cm.getColumnCount();
52965         for(var i = 0; i < colCount; i++){
52966             renderers[i] = cm.getRenderer(i);
52967         }
52968         return renderers;
52969     },
52970     
52971     getColumnIds : function(){
52972         var ids = [];
52973         var cm = this.grid.colModel;
52974         var colCount = cm.getColumnCount();
52975         for(var i = 0; i < colCount; i++){
52976             ids[i] = cm.getColumnId(i);
52977         }
52978         return ids;
52979     },
52980     
52981     getDataIndexes : function(){
52982         if(!this.indexMap){
52983             this.indexMap = this.buildIndexMap();
52984         }
52985         return this.indexMap.colToData;
52986     },
52987     
52988     getColumnIndexByDataIndex : function(dataIndex){
52989         if(!this.indexMap){
52990             this.indexMap = this.buildIndexMap();
52991         }
52992         return this.indexMap.dataToCol[dataIndex];
52993     },
52994     
52995     /**
52996      * Set a css style for a column dynamically. 
52997      * @param {Number} colIndex The index of the column
52998      * @param {String} name The css property name
52999      * @param {String} value The css value
53000      */
53001     setCSSStyle : function(colIndex, name, value){
53002         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53003         Roo.util.CSS.updateRule(selector, name, value);
53004     },
53005     
53006     generateRules : function(cm){
53007         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53008         Roo.util.CSS.removeStyleSheet(rulesId);
53009         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53010             var cid = cm.getColumnId(i);
53011             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53012                          this.tdSelector, cid, " {\n}\n",
53013                          this.hdSelector, cid, " {\n}\n",
53014                          this.splitSelector, cid, " {\n}\n");
53015         }
53016         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53017     }
53018 });/*
53019  * Based on:
53020  * Ext JS Library 1.1.1
53021  * Copyright(c) 2006-2007, Ext JS, LLC.
53022  *
53023  * Originally Released Under LGPL - original licence link has changed is not relivant.
53024  *
53025  * Fork - LGPL
53026  * <script type="text/javascript">
53027  */
53028
53029 // private
53030 // This is a support class used internally by the Grid components
53031 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53032     this.grid = grid;
53033     this.view = grid.getView();
53034     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53035     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53036     if(hd2){
53037         this.setHandleElId(Roo.id(hd));
53038         this.setOuterHandleElId(Roo.id(hd2));
53039     }
53040     this.scroll = false;
53041 };
53042 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53043     maxDragWidth: 120,
53044     getDragData : function(e){
53045         var t = Roo.lib.Event.getTarget(e);
53046         var h = this.view.findHeaderCell(t);
53047         if(h){
53048             return {ddel: h.firstChild, header:h};
53049         }
53050         return false;
53051     },
53052
53053     onInitDrag : function(e){
53054         this.view.headersDisabled = true;
53055         var clone = this.dragData.ddel.cloneNode(true);
53056         clone.id = Roo.id();
53057         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53058         this.proxy.update(clone);
53059         return true;
53060     },
53061
53062     afterValidDrop : function(){
53063         var v = this.view;
53064         setTimeout(function(){
53065             v.headersDisabled = false;
53066         }, 50);
53067     },
53068
53069     afterInvalidDrop : function(){
53070         var v = this.view;
53071         setTimeout(function(){
53072             v.headersDisabled = false;
53073         }, 50);
53074     }
53075 });
53076 /*
53077  * Based on:
53078  * Ext JS Library 1.1.1
53079  * Copyright(c) 2006-2007, Ext JS, LLC.
53080  *
53081  * Originally Released Under LGPL - original licence link has changed is not relivant.
53082  *
53083  * Fork - LGPL
53084  * <script type="text/javascript">
53085  */
53086 // private
53087 // This is a support class used internally by the Grid components
53088 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53089     this.grid = grid;
53090     this.view = grid.getView();
53091     // split the proxies so they don't interfere with mouse events
53092     this.proxyTop = Roo.DomHelper.append(document.body, {
53093         cls:"col-move-top", html:"&#160;"
53094     }, true);
53095     this.proxyBottom = Roo.DomHelper.append(document.body, {
53096         cls:"col-move-bottom", html:"&#160;"
53097     }, true);
53098     this.proxyTop.hide = this.proxyBottom.hide = function(){
53099         this.setLeftTop(-100,-100);
53100         this.setStyle("visibility", "hidden");
53101     };
53102     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53103     // temporarily disabled
53104     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53105     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53106 };
53107 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53108     proxyOffsets : [-4, -9],
53109     fly: Roo.Element.fly,
53110
53111     getTargetFromEvent : function(e){
53112         var t = Roo.lib.Event.getTarget(e);
53113         var cindex = this.view.findCellIndex(t);
53114         if(cindex !== false){
53115             return this.view.getHeaderCell(cindex);
53116         }
53117         return null;
53118     },
53119
53120     nextVisible : function(h){
53121         var v = this.view, cm = this.grid.colModel;
53122         h = h.nextSibling;
53123         while(h){
53124             if(!cm.isHidden(v.getCellIndex(h))){
53125                 return h;
53126             }
53127             h = h.nextSibling;
53128         }
53129         return null;
53130     },
53131
53132     prevVisible : function(h){
53133         var v = this.view, cm = this.grid.colModel;
53134         h = h.prevSibling;
53135         while(h){
53136             if(!cm.isHidden(v.getCellIndex(h))){
53137                 return h;
53138             }
53139             h = h.prevSibling;
53140         }
53141         return null;
53142     },
53143
53144     positionIndicator : function(h, n, e){
53145         var x = Roo.lib.Event.getPageX(e);
53146         var r = Roo.lib.Dom.getRegion(n.firstChild);
53147         var px, pt, py = r.top + this.proxyOffsets[1];
53148         if((r.right - x) <= (r.right-r.left)/2){
53149             px = r.right+this.view.borderWidth;
53150             pt = "after";
53151         }else{
53152             px = r.left;
53153             pt = "before";
53154         }
53155         var oldIndex = this.view.getCellIndex(h);
53156         var newIndex = this.view.getCellIndex(n);
53157
53158         if(this.grid.colModel.isFixed(newIndex)){
53159             return false;
53160         }
53161
53162         var locked = this.grid.colModel.isLocked(newIndex);
53163
53164         if(pt == "after"){
53165             newIndex++;
53166         }
53167         if(oldIndex < newIndex){
53168             newIndex--;
53169         }
53170         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53171             return false;
53172         }
53173         px +=  this.proxyOffsets[0];
53174         this.proxyTop.setLeftTop(px, py);
53175         this.proxyTop.show();
53176         if(!this.bottomOffset){
53177             this.bottomOffset = this.view.mainHd.getHeight();
53178         }
53179         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53180         this.proxyBottom.show();
53181         return pt;
53182     },
53183
53184     onNodeEnter : function(n, dd, e, data){
53185         if(data.header != n){
53186             this.positionIndicator(data.header, n, e);
53187         }
53188     },
53189
53190     onNodeOver : function(n, dd, e, data){
53191         var result = false;
53192         if(data.header != n){
53193             result = this.positionIndicator(data.header, n, e);
53194         }
53195         if(!result){
53196             this.proxyTop.hide();
53197             this.proxyBottom.hide();
53198         }
53199         return result ? this.dropAllowed : this.dropNotAllowed;
53200     },
53201
53202     onNodeOut : function(n, dd, e, data){
53203         this.proxyTop.hide();
53204         this.proxyBottom.hide();
53205     },
53206
53207     onNodeDrop : function(n, dd, e, data){
53208         var h = data.header;
53209         if(h != n){
53210             var cm = this.grid.colModel;
53211             var x = Roo.lib.Event.getPageX(e);
53212             var r = Roo.lib.Dom.getRegion(n.firstChild);
53213             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53214             var oldIndex = this.view.getCellIndex(h);
53215             var newIndex = this.view.getCellIndex(n);
53216             var locked = cm.isLocked(newIndex);
53217             if(pt == "after"){
53218                 newIndex++;
53219             }
53220             if(oldIndex < newIndex){
53221                 newIndex--;
53222             }
53223             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53224                 return false;
53225             }
53226             cm.setLocked(oldIndex, locked, true);
53227             cm.moveColumn(oldIndex, newIndex);
53228             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53229             return true;
53230         }
53231         return false;
53232     }
53233 });
53234 /*
53235  * Based on:
53236  * Ext JS Library 1.1.1
53237  * Copyright(c) 2006-2007, Ext JS, LLC.
53238  *
53239  * Originally Released Under LGPL - original licence link has changed is not relivant.
53240  *
53241  * Fork - LGPL
53242  * <script type="text/javascript">
53243  */
53244   
53245 /**
53246  * @class Roo.grid.GridView
53247  * @extends Roo.util.Observable
53248  *
53249  * @constructor
53250  * @param {Object} config
53251  */
53252 Roo.grid.GridView = function(config){
53253     Roo.grid.GridView.superclass.constructor.call(this);
53254     this.el = null;
53255
53256     Roo.apply(this, config);
53257 };
53258
53259 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53260
53261     unselectable :  'unselectable="on"',
53262     unselectableCls :  'x-unselectable',
53263     
53264     
53265     rowClass : "x-grid-row",
53266
53267     cellClass : "x-grid-col",
53268
53269     tdClass : "x-grid-td",
53270
53271     hdClass : "x-grid-hd",
53272
53273     splitClass : "x-grid-split",
53274
53275     sortClasses : ["sort-asc", "sort-desc"],
53276
53277     enableMoveAnim : false,
53278
53279     hlColor: "C3DAF9",
53280
53281     dh : Roo.DomHelper,
53282
53283     fly : Roo.Element.fly,
53284
53285     css : Roo.util.CSS,
53286
53287     borderWidth: 1,
53288
53289     splitOffset: 3,
53290
53291     scrollIncrement : 22,
53292
53293     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53294
53295     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53296
53297     bind : function(ds, cm){
53298         if(this.ds){
53299             this.ds.un("load", this.onLoad, this);
53300             this.ds.un("datachanged", this.onDataChange, this);
53301             this.ds.un("add", this.onAdd, this);
53302             this.ds.un("remove", this.onRemove, this);
53303             this.ds.un("update", this.onUpdate, this);
53304             this.ds.un("clear", this.onClear, this);
53305         }
53306         if(ds){
53307             ds.on("load", this.onLoad, this);
53308             ds.on("datachanged", this.onDataChange, this);
53309             ds.on("add", this.onAdd, this);
53310             ds.on("remove", this.onRemove, this);
53311             ds.on("update", this.onUpdate, this);
53312             ds.on("clear", this.onClear, this);
53313         }
53314         this.ds = ds;
53315
53316         if(this.cm){
53317             this.cm.un("widthchange", this.onColWidthChange, this);
53318             this.cm.un("headerchange", this.onHeaderChange, this);
53319             this.cm.un("hiddenchange", this.onHiddenChange, this);
53320             this.cm.un("columnmoved", this.onColumnMove, this);
53321             this.cm.un("columnlockchange", this.onColumnLock, this);
53322         }
53323         if(cm){
53324             this.generateRules(cm);
53325             cm.on("widthchange", this.onColWidthChange, this);
53326             cm.on("headerchange", this.onHeaderChange, this);
53327             cm.on("hiddenchange", this.onHiddenChange, this);
53328             cm.on("columnmoved", this.onColumnMove, this);
53329             cm.on("columnlockchange", this.onColumnLock, this);
53330         }
53331         this.cm = cm;
53332     },
53333
53334     init: function(grid){
53335         Roo.grid.GridView.superclass.init.call(this, grid);
53336
53337         this.bind(grid.dataSource, grid.colModel);
53338
53339         grid.on("headerclick", this.handleHeaderClick, this);
53340
53341         if(grid.trackMouseOver){
53342             grid.on("mouseover", this.onRowOver, this);
53343             grid.on("mouseout", this.onRowOut, this);
53344         }
53345         grid.cancelTextSelection = function(){};
53346         this.gridId = grid.id;
53347
53348         var tpls = this.templates || {};
53349
53350         if(!tpls.master){
53351             tpls.master = new Roo.Template(
53352                '<div class="x-grid" hidefocus="true">',
53353                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53354                   '<div class="x-grid-topbar"></div>',
53355                   '<div class="x-grid-scroller"><div></div></div>',
53356                   '<div class="x-grid-locked">',
53357                       '<div class="x-grid-header">{lockedHeader}</div>',
53358                       '<div class="x-grid-body">{lockedBody}</div>',
53359                   "</div>",
53360                   '<div class="x-grid-viewport">',
53361                       '<div class="x-grid-header">{header}</div>',
53362                       '<div class="x-grid-body">{body}</div>',
53363                   "</div>",
53364                   '<div class="x-grid-bottombar"></div>',
53365                  
53366                   '<div class="x-grid-resize-proxy">&#160;</div>',
53367                "</div>"
53368             );
53369             tpls.master.disableformats = true;
53370         }
53371
53372         if(!tpls.header){
53373             tpls.header = new Roo.Template(
53374                '<table border="0" cellspacing="0" cellpadding="0">',
53375                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53376                "</table>{splits}"
53377             );
53378             tpls.header.disableformats = true;
53379         }
53380         tpls.header.compile();
53381
53382         if(!tpls.hcell){
53383             tpls.hcell = new Roo.Template(
53384                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53385                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53386                 "</div></td>"
53387              );
53388              tpls.hcell.disableFormats = true;
53389         }
53390         tpls.hcell.compile();
53391
53392         if(!tpls.hsplit){
53393             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53394                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53395             tpls.hsplit.disableFormats = true;
53396         }
53397         tpls.hsplit.compile();
53398
53399         if(!tpls.body){
53400             tpls.body = new Roo.Template(
53401                '<table border="0" cellspacing="0" cellpadding="0">',
53402                "<tbody>{rows}</tbody>",
53403                "</table>"
53404             );
53405             tpls.body.disableFormats = true;
53406         }
53407         tpls.body.compile();
53408
53409         if(!tpls.row){
53410             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53411             tpls.row.disableFormats = true;
53412         }
53413         tpls.row.compile();
53414
53415         if(!tpls.cell){
53416             tpls.cell = new Roo.Template(
53417                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53418                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53419                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53420                 "</td>"
53421             );
53422             tpls.cell.disableFormats = true;
53423         }
53424         tpls.cell.compile();
53425
53426         this.templates = tpls;
53427     },
53428
53429     // remap these for backwards compat
53430     onColWidthChange : function(){
53431         this.updateColumns.apply(this, arguments);
53432     },
53433     onHeaderChange : function(){
53434         this.updateHeaders.apply(this, arguments);
53435     }, 
53436     onHiddenChange : function(){
53437         this.handleHiddenChange.apply(this, arguments);
53438     },
53439     onColumnMove : function(){
53440         this.handleColumnMove.apply(this, arguments);
53441     },
53442     onColumnLock : function(){
53443         this.handleLockChange.apply(this, arguments);
53444     },
53445
53446     onDataChange : function(){
53447         this.refresh();
53448         this.updateHeaderSortState();
53449     },
53450
53451     onClear : function(){
53452         this.refresh();
53453     },
53454
53455     onUpdate : function(ds, record){
53456         this.refreshRow(record);
53457     },
53458
53459     refreshRow : function(record){
53460         var ds = this.ds, index;
53461         if(typeof record == 'number'){
53462             index = record;
53463             record = ds.getAt(index);
53464         }else{
53465             index = ds.indexOf(record);
53466         }
53467         this.insertRows(ds, index, index, true);
53468         this.onRemove(ds, record, index+1, true);
53469         this.syncRowHeights(index, index);
53470         this.layout();
53471         this.fireEvent("rowupdated", this, index, record);
53472     },
53473
53474     onAdd : function(ds, records, index){
53475         this.insertRows(ds, index, index + (records.length-1));
53476     },
53477
53478     onRemove : function(ds, record, index, isUpdate){
53479         if(isUpdate !== true){
53480             this.fireEvent("beforerowremoved", this, index, record);
53481         }
53482         var bt = this.getBodyTable(), lt = this.getLockedTable();
53483         if(bt.rows[index]){
53484             bt.firstChild.removeChild(bt.rows[index]);
53485         }
53486         if(lt.rows[index]){
53487             lt.firstChild.removeChild(lt.rows[index]);
53488         }
53489         if(isUpdate !== true){
53490             this.stripeRows(index);
53491             this.syncRowHeights(index, index);
53492             this.layout();
53493             this.fireEvent("rowremoved", this, index, record);
53494         }
53495     },
53496
53497     onLoad : function(){
53498         this.scrollToTop();
53499     },
53500
53501     /**
53502      * Scrolls the grid to the top
53503      */
53504     scrollToTop : function(){
53505         if(this.scroller){
53506             this.scroller.dom.scrollTop = 0;
53507             this.syncScroll();
53508         }
53509     },
53510
53511     /**
53512      * Gets a panel in the header of the grid that can be used for toolbars etc.
53513      * After modifying the contents of this panel a call to grid.autoSize() may be
53514      * required to register any changes in size.
53515      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53516      * @return Roo.Element
53517      */
53518     getHeaderPanel : function(doShow){
53519         if(doShow){
53520             this.headerPanel.show();
53521         }
53522         return this.headerPanel;
53523     },
53524
53525     /**
53526      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53527      * After modifying the contents of this panel a call to grid.autoSize() may be
53528      * required to register any changes in size.
53529      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53530      * @return Roo.Element
53531      */
53532     getFooterPanel : function(doShow){
53533         if(doShow){
53534             this.footerPanel.show();
53535         }
53536         return this.footerPanel;
53537     },
53538
53539     initElements : function(){
53540         var E = Roo.Element;
53541         var el = this.grid.getGridEl().dom.firstChild;
53542         var cs = el.childNodes;
53543
53544         this.el = new E(el);
53545         
53546          this.focusEl = new E(el.firstChild);
53547         this.focusEl.swallowEvent("click", true);
53548         
53549         this.headerPanel = new E(cs[1]);
53550         this.headerPanel.enableDisplayMode("block");
53551
53552         this.scroller = new E(cs[2]);
53553         this.scrollSizer = new E(this.scroller.dom.firstChild);
53554
53555         this.lockedWrap = new E(cs[3]);
53556         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53557         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53558
53559         this.mainWrap = new E(cs[4]);
53560         this.mainHd = new E(this.mainWrap.dom.firstChild);
53561         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53562
53563         this.footerPanel = new E(cs[5]);
53564         this.footerPanel.enableDisplayMode("block");
53565
53566         this.resizeProxy = new E(cs[6]);
53567
53568         this.headerSelector = String.format(
53569            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53570            this.lockedHd.id, this.mainHd.id
53571         );
53572
53573         this.splitterSelector = String.format(
53574            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53575            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53576         );
53577     },
53578     idToCssName : function(s)
53579     {
53580         return s.replace(/[^a-z0-9]+/ig, '-');
53581     },
53582
53583     getHeaderCell : function(index){
53584         return Roo.DomQuery.select(this.headerSelector)[index];
53585     },
53586
53587     getHeaderCellMeasure : function(index){
53588         return this.getHeaderCell(index).firstChild;
53589     },
53590
53591     getHeaderCellText : function(index){
53592         return this.getHeaderCell(index).firstChild.firstChild;
53593     },
53594
53595     getLockedTable : function(){
53596         return this.lockedBody.dom.firstChild;
53597     },
53598
53599     getBodyTable : function(){
53600         return this.mainBody.dom.firstChild;
53601     },
53602
53603     getLockedRow : function(index){
53604         return this.getLockedTable().rows[index];
53605     },
53606
53607     getRow : function(index){
53608         return this.getBodyTable().rows[index];
53609     },
53610
53611     getRowComposite : function(index){
53612         if(!this.rowEl){
53613             this.rowEl = new Roo.CompositeElementLite();
53614         }
53615         var els = [], lrow, mrow;
53616         if(lrow = this.getLockedRow(index)){
53617             els.push(lrow);
53618         }
53619         if(mrow = this.getRow(index)){
53620             els.push(mrow);
53621         }
53622         this.rowEl.elements = els;
53623         return this.rowEl;
53624     },
53625     /**
53626      * Gets the 'td' of the cell
53627      * 
53628      * @param {Integer} rowIndex row to select
53629      * @param {Integer} colIndex column to select
53630      * 
53631      * @return {Object} 
53632      */
53633     getCell : function(rowIndex, colIndex){
53634         var locked = this.cm.getLockedCount();
53635         var source;
53636         if(colIndex < locked){
53637             source = this.lockedBody.dom.firstChild;
53638         }else{
53639             source = this.mainBody.dom.firstChild;
53640             colIndex -= locked;
53641         }
53642         return source.rows[rowIndex].childNodes[colIndex];
53643     },
53644
53645     getCellText : function(rowIndex, colIndex){
53646         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53647     },
53648
53649     getCellBox : function(cell){
53650         var b = this.fly(cell).getBox();
53651         if(Roo.isOpera){ // opera fails to report the Y
53652             b.y = cell.offsetTop + this.mainBody.getY();
53653         }
53654         return b;
53655     },
53656
53657     getCellIndex : function(cell){
53658         var id = String(cell.className).match(this.cellRE);
53659         if(id){
53660             return parseInt(id[1], 10);
53661         }
53662         return 0;
53663     },
53664
53665     findHeaderIndex : function(n){
53666         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53667         return r ? this.getCellIndex(r) : false;
53668     },
53669
53670     findHeaderCell : function(n){
53671         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53672         return r ? r : false;
53673     },
53674
53675     findRowIndex : function(n){
53676         if(!n){
53677             return false;
53678         }
53679         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53680         return r ? r.rowIndex : false;
53681     },
53682
53683     findCellIndex : function(node){
53684         var stop = this.el.dom;
53685         while(node && node != stop){
53686             if(this.findRE.test(node.className)){
53687                 return this.getCellIndex(node);
53688             }
53689             node = node.parentNode;
53690         }
53691         return false;
53692     },
53693
53694     getColumnId : function(index){
53695         return this.cm.getColumnId(index);
53696     },
53697
53698     getSplitters : function()
53699     {
53700         if(this.splitterSelector){
53701            return Roo.DomQuery.select(this.splitterSelector);
53702         }else{
53703             return null;
53704       }
53705     },
53706
53707     getSplitter : function(index){
53708         return this.getSplitters()[index];
53709     },
53710
53711     onRowOver : function(e, t){
53712         var row;
53713         if((row = this.findRowIndex(t)) !== false){
53714             this.getRowComposite(row).addClass("x-grid-row-over");
53715         }
53716     },
53717
53718     onRowOut : function(e, t){
53719         var row;
53720         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53721             this.getRowComposite(row).removeClass("x-grid-row-over");
53722         }
53723     },
53724
53725     renderHeaders : function(){
53726         var cm = this.cm;
53727         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53728         var cb = [], lb = [], sb = [], lsb = [], p = {};
53729         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53730             p.cellId = "x-grid-hd-0-" + i;
53731             p.splitId = "x-grid-csplit-0-" + i;
53732             p.id = cm.getColumnId(i);
53733             p.title = cm.getColumnTooltip(i) || "";
53734             p.value = cm.getColumnHeader(i) || "";
53735             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53736             if(!cm.isLocked(i)){
53737                 cb[cb.length] = ct.apply(p);
53738                 sb[sb.length] = st.apply(p);
53739             }else{
53740                 lb[lb.length] = ct.apply(p);
53741                 lsb[lsb.length] = st.apply(p);
53742             }
53743         }
53744         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53745                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53746     },
53747
53748     updateHeaders : function(){
53749         var html = this.renderHeaders();
53750         this.lockedHd.update(html[0]);
53751         this.mainHd.update(html[1]);
53752     },
53753
53754     /**
53755      * Focuses the specified row.
53756      * @param {Number} row The row index
53757      */
53758     focusRow : function(row)
53759     {
53760         //Roo.log('GridView.focusRow');
53761         var x = this.scroller.dom.scrollLeft;
53762         this.focusCell(row, 0, false);
53763         this.scroller.dom.scrollLeft = x;
53764     },
53765
53766     /**
53767      * Focuses the specified cell.
53768      * @param {Number} row The row index
53769      * @param {Number} col The column index
53770      * @param {Boolean} hscroll false to disable horizontal scrolling
53771      */
53772     focusCell : function(row, col, hscroll)
53773     {
53774         //Roo.log('GridView.focusCell');
53775         var el = this.ensureVisible(row, col, hscroll);
53776         this.focusEl.alignTo(el, "tl-tl");
53777         if(Roo.isGecko){
53778             this.focusEl.focus();
53779         }else{
53780             this.focusEl.focus.defer(1, this.focusEl);
53781         }
53782     },
53783
53784     /**
53785      * Scrolls the specified cell into view
53786      * @param {Number} row The row index
53787      * @param {Number} col The column index
53788      * @param {Boolean} hscroll false to disable horizontal scrolling
53789      */
53790     ensureVisible : function(row, col, hscroll)
53791     {
53792         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53793         //return null; //disable for testing.
53794         if(typeof row != "number"){
53795             row = row.rowIndex;
53796         }
53797         if(row < 0 && row >= this.ds.getCount()){
53798             return  null;
53799         }
53800         col = (col !== undefined ? col : 0);
53801         var cm = this.grid.colModel;
53802         while(cm.isHidden(col)){
53803             col++;
53804         }
53805
53806         var el = this.getCell(row, col);
53807         if(!el){
53808             return null;
53809         }
53810         var c = this.scroller.dom;
53811
53812         var ctop = parseInt(el.offsetTop, 10);
53813         var cleft = parseInt(el.offsetLeft, 10);
53814         var cbot = ctop + el.offsetHeight;
53815         var cright = cleft + el.offsetWidth;
53816         
53817         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53818         var stop = parseInt(c.scrollTop, 10);
53819         var sleft = parseInt(c.scrollLeft, 10);
53820         var sbot = stop + ch;
53821         var sright = sleft + c.clientWidth;
53822         /*
53823         Roo.log('GridView.ensureVisible:' +
53824                 ' ctop:' + ctop +
53825                 ' c.clientHeight:' + c.clientHeight +
53826                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53827                 ' stop:' + stop +
53828                 ' cbot:' + cbot +
53829                 ' sbot:' + sbot +
53830                 ' ch:' + ch  
53831                 );
53832         */
53833         if(ctop < stop){
53834              c.scrollTop = ctop;
53835             //Roo.log("set scrolltop to ctop DISABLE?");
53836         }else if(cbot > sbot){
53837             //Roo.log("set scrolltop to cbot-ch");
53838             c.scrollTop = cbot-ch;
53839         }
53840         
53841         if(hscroll !== false){
53842             if(cleft < sleft){
53843                 c.scrollLeft = cleft;
53844             }else if(cright > sright){
53845                 c.scrollLeft = cright-c.clientWidth;
53846             }
53847         }
53848          
53849         return el;
53850     },
53851
53852     updateColumns : function(){
53853         this.grid.stopEditing();
53854         var cm = this.grid.colModel, colIds = this.getColumnIds();
53855         //var totalWidth = cm.getTotalWidth();
53856         var pos = 0;
53857         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53858             //if(cm.isHidden(i)) continue;
53859             var w = cm.getColumnWidth(i);
53860             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53861             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53862         }
53863         this.updateSplitters();
53864     },
53865
53866     generateRules : function(cm){
53867         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53868         Roo.util.CSS.removeStyleSheet(rulesId);
53869         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53870             var cid = cm.getColumnId(i);
53871             var align = '';
53872             if(cm.config[i].align){
53873                 align = 'text-align:'+cm.config[i].align+';';
53874             }
53875             var hidden = '';
53876             if(cm.isHidden(i)){
53877                 hidden = 'display:none;';
53878             }
53879             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53880             ruleBuf.push(
53881                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53882                     this.hdSelector, cid, " {\n", align, width, "}\n",
53883                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53884                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53885         }
53886         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53887     },
53888
53889     updateSplitters : function(){
53890         var cm = this.cm, s = this.getSplitters();
53891         if(s){ // splitters not created yet
53892             var pos = 0, locked = true;
53893             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53894                 if(cm.isHidden(i)) continue;
53895                 var w = cm.getColumnWidth(i); // make sure it's a number
53896                 if(!cm.isLocked(i) && locked){
53897                     pos = 0;
53898                     locked = false;
53899                 }
53900                 pos += w;
53901                 s[i].style.left = (pos-this.splitOffset) + "px";
53902             }
53903         }
53904     },
53905
53906     handleHiddenChange : function(colModel, colIndex, hidden){
53907         if(hidden){
53908             this.hideColumn(colIndex);
53909         }else{
53910             this.unhideColumn(colIndex);
53911         }
53912     },
53913
53914     hideColumn : function(colIndex){
53915         var cid = this.getColumnId(colIndex);
53916         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53917         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53918         if(Roo.isSafari){
53919             this.updateHeaders();
53920         }
53921         this.updateSplitters();
53922         this.layout();
53923     },
53924
53925     unhideColumn : function(colIndex){
53926         var cid = this.getColumnId(colIndex);
53927         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53928         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53929
53930         if(Roo.isSafari){
53931             this.updateHeaders();
53932         }
53933         this.updateSplitters();
53934         this.layout();
53935     },
53936
53937     insertRows : function(dm, firstRow, lastRow, isUpdate){
53938         if(firstRow == 0 && lastRow == dm.getCount()-1){
53939             this.refresh();
53940         }else{
53941             if(!isUpdate){
53942                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53943             }
53944             var s = this.getScrollState();
53945             var markup = this.renderRows(firstRow, lastRow);
53946             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53947             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53948             this.restoreScroll(s);
53949             if(!isUpdate){
53950                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53951                 this.syncRowHeights(firstRow, lastRow);
53952                 this.stripeRows(firstRow);
53953                 this.layout();
53954             }
53955         }
53956     },
53957
53958     bufferRows : function(markup, target, index){
53959         var before = null, trows = target.rows, tbody = target.tBodies[0];
53960         if(index < trows.length){
53961             before = trows[index];
53962         }
53963         var b = document.createElement("div");
53964         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53965         var rows = b.firstChild.rows;
53966         for(var i = 0, len = rows.length; i < len; i++){
53967             if(before){
53968                 tbody.insertBefore(rows[0], before);
53969             }else{
53970                 tbody.appendChild(rows[0]);
53971             }
53972         }
53973         b.innerHTML = "";
53974         b = null;
53975     },
53976
53977     deleteRows : function(dm, firstRow, lastRow){
53978         if(dm.getRowCount()<1){
53979             this.fireEvent("beforerefresh", this);
53980             this.mainBody.update("");
53981             this.lockedBody.update("");
53982             this.fireEvent("refresh", this);
53983         }else{
53984             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53985             var bt = this.getBodyTable();
53986             var tbody = bt.firstChild;
53987             var rows = bt.rows;
53988             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53989                 tbody.removeChild(rows[firstRow]);
53990             }
53991             this.stripeRows(firstRow);
53992             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53993         }
53994     },
53995
53996     updateRows : function(dataSource, firstRow, lastRow){
53997         var s = this.getScrollState();
53998         this.refresh();
53999         this.restoreScroll(s);
54000     },
54001
54002     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54003         if(!noRefresh){
54004            this.refresh();
54005         }
54006         this.updateHeaderSortState();
54007     },
54008
54009     getScrollState : function(){
54010         
54011         var sb = this.scroller.dom;
54012         return {left: sb.scrollLeft, top: sb.scrollTop};
54013     },
54014
54015     stripeRows : function(startRow){
54016         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54017             return;
54018         }
54019         startRow = startRow || 0;
54020         var rows = this.getBodyTable().rows;
54021         var lrows = this.getLockedTable().rows;
54022         var cls = ' x-grid-row-alt ';
54023         for(var i = startRow, len = rows.length; i < len; i++){
54024             var row = rows[i], lrow = lrows[i];
54025             var isAlt = ((i+1) % 2 == 0);
54026             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54027             if(isAlt == hasAlt){
54028                 continue;
54029             }
54030             if(isAlt){
54031                 row.className += " x-grid-row-alt";
54032             }else{
54033                 row.className = row.className.replace("x-grid-row-alt", "");
54034             }
54035             if(lrow){
54036                 lrow.className = row.className;
54037             }
54038         }
54039     },
54040
54041     restoreScroll : function(state){
54042         //Roo.log('GridView.restoreScroll');
54043         var sb = this.scroller.dom;
54044         sb.scrollLeft = state.left;
54045         sb.scrollTop = state.top;
54046         this.syncScroll();
54047     },
54048
54049     syncScroll : function(){
54050         //Roo.log('GridView.syncScroll');
54051         var sb = this.scroller.dom;
54052         var sh = this.mainHd.dom;
54053         var bs = this.mainBody.dom;
54054         var lv = this.lockedBody.dom;
54055         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54056         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54057     },
54058
54059     handleScroll : function(e){
54060         this.syncScroll();
54061         var sb = this.scroller.dom;
54062         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54063         e.stopEvent();
54064     },
54065
54066     handleWheel : function(e){
54067         var d = e.getWheelDelta();
54068         this.scroller.dom.scrollTop -= d*22;
54069         // set this here to prevent jumpy scrolling on large tables
54070         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54071         e.stopEvent();
54072     },
54073
54074     renderRows : function(startRow, endRow){
54075         // pull in all the crap needed to render rows
54076         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54077         var colCount = cm.getColumnCount();
54078
54079         if(ds.getCount() < 1){
54080             return ["", ""];
54081         }
54082
54083         // build a map for all the columns
54084         var cs = [];
54085         for(var i = 0; i < colCount; i++){
54086             var name = cm.getDataIndex(i);
54087             cs[i] = {
54088                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54089                 renderer : cm.getRenderer(i),
54090                 id : cm.getColumnId(i),
54091                 locked : cm.isLocked(i)
54092             };
54093         }
54094
54095         startRow = startRow || 0;
54096         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54097
54098         // records to render
54099         var rs = ds.getRange(startRow, endRow);
54100
54101         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54102     },
54103
54104     // As much as I hate to duplicate code, this was branched because FireFox really hates
54105     // [].join("") on strings. The performance difference was substantial enough to
54106     // branch this function
54107     doRender : Roo.isGecko ?
54108             function(cs, rs, ds, startRow, colCount, stripe){
54109                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54110                 // buffers
54111                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54112                 
54113                 var hasListener = this.grid.hasListener('rowclass');
54114                 var rowcfg = {};
54115                 for(var j = 0, len = rs.length; j < len; j++){
54116                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54117                     for(var i = 0; i < colCount; i++){
54118                         c = cs[i];
54119                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54120                         p.id = c.id;
54121                         p.css = p.attr = "";
54122                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54123                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54124                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54125                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54126                         }
54127                         var markup = ct.apply(p);
54128                         if(!c.locked){
54129                             cb+= markup;
54130                         }else{
54131                             lcb+= markup;
54132                         }
54133                     }
54134                     var alt = [];
54135                     if(stripe && ((rowIndex+1) % 2 == 0)){
54136                         alt.push("x-grid-row-alt")
54137                     }
54138                     if(r.dirty){
54139                         alt.push(  " x-grid-dirty-row");
54140                     }
54141                     rp.cells = lcb;
54142                     if(this.getRowClass){
54143                         alt.push(this.getRowClass(r, rowIndex));
54144                     }
54145                     if (hasListener) {
54146                         rowcfg = {
54147                              
54148                             record: r,
54149                             rowIndex : rowIndex,
54150                             rowClass : ''
54151                         }
54152                         this.grid.fireEvent('rowclass', this, rowcfg);
54153                         alt.push(rowcfg.rowClass);
54154                     }
54155                     rp.alt = alt.join(" ");
54156                     lbuf+= rt.apply(rp);
54157                     rp.cells = cb;
54158                     buf+=  rt.apply(rp);
54159                 }
54160                 return [lbuf, buf];
54161             } :
54162             function(cs, rs, ds, startRow, colCount, stripe){
54163                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54164                 // buffers
54165                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54166                 var hasListener = this.grid.hasListener('rowclass');
54167  
54168                 var rowcfg = {};
54169                 for(var j = 0, len = rs.length; j < len; j++){
54170                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54171                     for(var i = 0; i < colCount; i++){
54172                         c = cs[i];
54173                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54174                         p.id = c.id;
54175                         p.css = p.attr = "";
54176                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54177                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54178                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54179                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54180                         }
54181                         
54182                         var markup = ct.apply(p);
54183                         if(!c.locked){
54184                             cb[cb.length] = markup;
54185                         }else{
54186                             lcb[lcb.length] = markup;
54187                         }
54188                     }
54189                     var alt = [];
54190                     if(stripe && ((rowIndex+1) % 2 == 0)){
54191                         alt.push( "x-grid-row-alt");
54192                     }
54193                     if(r.dirty){
54194                         alt.push(" x-grid-dirty-row");
54195                     }
54196                     rp.cells = lcb;
54197                     if(this.getRowClass){
54198                         alt.push( this.getRowClass(r, rowIndex));
54199                     }
54200                     if (hasListener) {
54201                         rowcfg = {
54202                              
54203                             record: r,
54204                             rowIndex : rowIndex,
54205                             rowClass : ''
54206                         }
54207                         this.grid.fireEvent('rowclass', this, rowcfg);
54208                         alt.push(rowcfg.rowClass);
54209                     }
54210                     rp.alt = alt.join(" ");
54211                     rp.cells = lcb.join("");
54212                     lbuf[lbuf.length] = rt.apply(rp);
54213                     rp.cells = cb.join("");
54214                     buf[buf.length] =  rt.apply(rp);
54215                 }
54216                 return [lbuf.join(""), buf.join("")];
54217             },
54218
54219     renderBody : function(){
54220         var markup = this.renderRows();
54221         var bt = this.templates.body;
54222         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54223     },
54224
54225     /**
54226      * Refreshes the grid
54227      * @param {Boolean} headersToo
54228      */
54229     refresh : function(headersToo){
54230         this.fireEvent("beforerefresh", this);
54231         this.grid.stopEditing();
54232         var result = this.renderBody();
54233         this.lockedBody.update(result[0]);
54234         this.mainBody.update(result[1]);
54235         if(headersToo === true){
54236             this.updateHeaders();
54237             this.updateColumns();
54238             this.updateSplitters();
54239             this.updateHeaderSortState();
54240         }
54241         this.syncRowHeights();
54242         this.layout();
54243         this.fireEvent("refresh", this);
54244     },
54245
54246     handleColumnMove : function(cm, oldIndex, newIndex){
54247         this.indexMap = null;
54248         var s = this.getScrollState();
54249         this.refresh(true);
54250         this.restoreScroll(s);
54251         this.afterMove(newIndex);
54252     },
54253
54254     afterMove : function(colIndex){
54255         if(this.enableMoveAnim && Roo.enableFx){
54256             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54257         }
54258         // if multisort - fix sortOrder, and reload..
54259         if (this.grid.dataSource.multiSort) {
54260             // the we can call sort again..
54261             var dm = this.grid.dataSource;
54262             var cm = this.grid.colModel;
54263             var so = [];
54264             for(var i = 0; i < cm.config.length; i++ ) {
54265                 
54266                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54267                     continue; // dont' bother, it's not in sort list or being set.
54268                 }
54269                 
54270                 so.push(cm.config[i].dataIndex);
54271             };
54272             dm.sortOrder = so;
54273             dm.load(dm.lastOptions);
54274             
54275             
54276         }
54277         
54278     },
54279
54280     updateCell : function(dm, rowIndex, dataIndex){
54281         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54282         if(typeof colIndex == "undefined"){ // not present in grid
54283             return;
54284         }
54285         var cm = this.grid.colModel;
54286         var cell = this.getCell(rowIndex, colIndex);
54287         var cellText = this.getCellText(rowIndex, colIndex);
54288
54289         var p = {
54290             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54291             id : cm.getColumnId(colIndex),
54292             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54293         };
54294         var renderer = cm.getRenderer(colIndex);
54295         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54296         if(typeof val == "undefined" || val === "") val = "&#160;";
54297         cellText.innerHTML = val;
54298         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54299         this.syncRowHeights(rowIndex, rowIndex);
54300     },
54301
54302     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54303         var maxWidth = 0;
54304         if(this.grid.autoSizeHeaders){
54305             var h = this.getHeaderCellMeasure(colIndex);
54306             maxWidth = Math.max(maxWidth, h.scrollWidth);
54307         }
54308         var tb, index;
54309         if(this.cm.isLocked(colIndex)){
54310             tb = this.getLockedTable();
54311             index = colIndex;
54312         }else{
54313             tb = this.getBodyTable();
54314             index = colIndex - this.cm.getLockedCount();
54315         }
54316         if(tb && tb.rows){
54317             var rows = tb.rows;
54318             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54319             for(var i = 0; i < stopIndex; i++){
54320                 var cell = rows[i].childNodes[index].firstChild;
54321                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54322             }
54323         }
54324         return maxWidth + /*margin for error in IE*/ 5;
54325     },
54326     /**
54327      * Autofit a column to its content.
54328      * @param {Number} colIndex
54329      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54330      */
54331      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54332          if(this.cm.isHidden(colIndex)){
54333              return; // can't calc a hidden column
54334          }
54335         if(forceMinSize){
54336             var cid = this.cm.getColumnId(colIndex);
54337             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54338            if(this.grid.autoSizeHeaders){
54339                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54340            }
54341         }
54342         var newWidth = this.calcColumnWidth(colIndex);
54343         this.cm.setColumnWidth(colIndex,
54344             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54345         if(!suppressEvent){
54346             this.grid.fireEvent("columnresize", colIndex, newWidth);
54347         }
54348     },
54349
54350     /**
54351      * Autofits all columns to their content and then expands to fit any extra space in the grid
54352      */
54353      autoSizeColumns : function(){
54354         var cm = this.grid.colModel;
54355         var colCount = cm.getColumnCount();
54356         for(var i = 0; i < colCount; i++){
54357             this.autoSizeColumn(i, true, true);
54358         }
54359         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54360             this.fitColumns();
54361         }else{
54362             this.updateColumns();
54363             this.layout();
54364         }
54365     },
54366
54367     /**
54368      * Autofits all columns to the grid's width proportionate with their current size
54369      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54370      */
54371     fitColumns : function(reserveScrollSpace){
54372         var cm = this.grid.colModel;
54373         var colCount = cm.getColumnCount();
54374         var cols = [];
54375         var width = 0;
54376         var i, w;
54377         for (i = 0; i < colCount; i++){
54378             if(!cm.isHidden(i) && !cm.isFixed(i)){
54379                 w = cm.getColumnWidth(i);
54380                 cols.push(i);
54381                 cols.push(w);
54382                 width += w;
54383             }
54384         }
54385         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54386         if(reserveScrollSpace){
54387             avail -= 17;
54388         }
54389         var frac = (avail - cm.getTotalWidth())/width;
54390         while (cols.length){
54391             w = cols.pop();
54392             i = cols.pop();
54393             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54394         }
54395         this.updateColumns();
54396         this.layout();
54397     },
54398
54399     onRowSelect : function(rowIndex){
54400         var row = this.getRowComposite(rowIndex);
54401         row.addClass("x-grid-row-selected");
54402     },
54403
54404     onRowDeselect : function(rowIndex){
54405         var row = this.getRowComposite(rowIndex);
54406         row.removeClass("x-grid-row-selected");
54407     },
54408
54409     onCellSelect : function(row, col){
54410         var cell = this.getCell(row, col);
54411         if(cell){
54412             Roo.fly(cell).addClass("x-grid-cell-selected");
54413         }
54414     },
54415
54416     onCellDeselect : function(row, col){
54417         var cell = this.getCell(row, col);
54418         if(cell){
54419             Roo.fly(cell).removeClass("x-grid-cell-selected");
54420         }
54421     },
54422
54423     updateHeaderSortState : function(){
54424         
54425         // sort state can be single { field: xxx, direction : yyy}
54426         // or   { xxx=>ASC , yyy : DESC ..... }
54427         
54428         var mstate = {};
54429         if (!this.ds.multiSort) { 
54430             var state = this.ds.getSortState();
54431             if(!state){
54432                 return;
54433             }
54434             mstate[state.field] = state.direction;
54435             // FIXME... - this is not used here.. but might be elsewhere..
54436             this.sortState = state;
54437             
54438         } else {
54439             mstate = this.ds.sortToggle;
54440         }
54441         //remove existing sort classes..
54442         
54443         var sc = this.sortClasses;
54444         var hds = this.el.select(this.headerSelector).removeClass(sc);
54445         
54446         for(var f in mstate) {
54447         
54448             var sortColumn = this.cm.findColumnIndex(f);
54449             
54450             if(sortColumn != -1){
54451                 var sortDir = mstate[f];        
54452                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54453             }
54454         }
54455         
54456          
54457         
54458     },
54459
54460
54461     handleHeaderClick : function(g, index,e){
54462         
54463         Roo.log("header click");
54464         
54465         if (Roo.isTouch) {
54466             // touch events on header are handled by context
54467             this.handleHdCtx(g,index,e);
54468             return;
54469         }
54470         
54471         
54472         if(this.headersDisabled){
54473             return;
54474         }
54475         var dm = g.dataSource, cm = g.colModel;
54476         if(!cm.isSortable(index)){
54477             return;
54478         }
54479         g.stopEditing();
54480         
54481         if (dm.multiSort) {
54482             // update the sortOrder
54483             var so = [];
54484             for(var i = 0; i < cm.config.length; i++ ) {
54485                 
54486                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54487                     continue; // dont' bother, it's not in sort list or being set.
54488                 }
54489                 
54490                 so.push(cm.config[i].dataIndex);
54491             };
54492             dm.sortOrder = so;
54493         }
54494         
54495         
54496         dm.sort(cm.getDataIndex(index));
54497     },
54498
54499
54500     destroy : function(){
54501         if(this.colMenu){
54502             this.colMenu.removeAll();
54503             Roo.menu.MenuMgr.unregister(this.colMenu);
54504             this.colMenu.getEl().remove();
54505             delete this.colMenu;
54506         }
54507         if(this.hmenu){
54508             this.hmenu.removeAll();
54509             Roo.menu.MenuMgr.unregister(this.hmenu);
54510             this.hmenu.getEl().remove();
54511             delete this.hmenu;
54512         }
54513         if(this.grid.enableColumnMove){
54514             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54515             if(dds){
54516                 for(var dd in dds){
54517                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54518                         var elid = dds[dd].dragElId;
54519                         dds[dd].unreg();
54520                         Roo.get(elid).remove();
54521                     } else if(dds[dd].config.isTarget){
54522                         dds[dd].proxyTop.remove();
54523                         dds[dd].proxyBottom.remove();
54524                         dds[dd].unreg();
54525                     }
54526                     if(Roo.dd.DDM.locationCache[dd]){
54527                         delete Roo.dd.DDM.locationCache[dd];
54528                     }
54529                 }
54530                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54531             }
54532         }
54533         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54534         this.bind(null, null);
54535         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54536     },
54537
54538     handleLockChange : function(){
54539         this.refresh(true);
54540     },
54541
54542     onDenyColumnLock : function(){
54543
54544     },
54545
54546     onDenyColumnHide : function(){
54547
54548     },
54549
54550     handleHdMenuClick : function(item){
54551         var index = this.hdCtxIndex;
54552         var cm = this.cm, ds = this.ds;
54553         switch(item.id){
54554             case "asc":
54555                 ds.sort(cm.getDataIndex(index), "ASC");
54556                 break;
54557             case "desc":
54558                 ds.sort(cm.getDataIndex(index), "DESC");
54559                 break;
54560             case "lock":
54561                 var lc = cm.getLockedCount();
54562                 if(cm.getColumnCount(true) <= lc+1){
54563                     this.onDenyColumnLock();
54564                     return;
54565                 }
54566                 if(lc != index){
54567                     cm.setLocked(index, true, true);
54568                     cm.moveColumn(index, lc);
54569                     this.grid.fireEvent("columnmove", index, lc);
54570                 }else{
54571                     cm.setLocked(index, true);
54572                 }
54573             break;
54574             case "unlock":
54575                 var lc = cm.getLockedCount();
54576                 if((lc-1) != index){
54577                     cm.setLocked(index, false, true);
54578                     cm.moveColumn(index, lc-1);
54579                     this.grid.fireEvent("columnmove", index, lc-1);
54580                 }else{
54581                     cm.setLocked(index, false);
54582                 }
54583             break;
54584             case 'wider': // used to expand cols on touch..
54585             case 'narrow':
54586                 var cw = cm.getColumnWidth(index);
54587                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54588                 cw = Math.max(0, cw);
54589                 cw = Math.min(cw,4000);
54590                 cm.setColumnWidth(index, cw);
54591                 break;
54592                 
54593             default:
54594                 index = cm.getIndexById(item.id.substr(4));
54595                 if(index != -1){
54596                     if(item.checked && cm.getColumnCount(true) <= 1){
54597                         this.onDenyColumnHide();
54598                         return false;
54599                     }
54600                     cm.setHidden(index, item.checked);
54601                 }
54602         }
54603         return true;
54604     },
54605
54606     beforeColMenuShow : function(){
54607         var cm = this.cm,  colCount = cm.getColumnCount();
54608         this.colMenu.removeAll();
54609         for(var i = 0; i < colCount; i++){
54610             this.colMenu.add(new Roo.menu.CheckItem({
54611                 id: "col-"+cm.getColumnId(i),
54612                 text: cm.getColumnHeader(i),
54613                 checked: !cm.isHidden(i),
54614                 hideOnClick:false
54615             }));
54616         }
54617     },
54618
54619     handleHdCtx : function(g, index, e){
54620         e.stopEvent();
54621         var hd = this.getHeaderCell(index);
54622         this.hdCtxIndex = index;
54623         var ms = this.hmenu.items, cm = this.cm;
54624         ms.get("asc").setDisabled(!cm.isSortable(index));
54625         ms.get("desc").setDisabled(!cm.isSortable(index));
54626         if(this.grid.enableColLock !== false){
54627             ms.get("lock").setDisabled(cm.isLocked(index));
54628             ms.get("unlock").setDisabled(!cm.isLocked(index));
54629         }
54630         this.hmenu.show(hd, "tl-bl");
54631     },
54632
54633     handleHdOver : function(e){
54634         var hd = this.findHeaderCell(e.getTarget());
54635         if(hd && !this.headersDisabled){
54636             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54637                this.fly(hd).addClass("x-grid-hd-over");
54638             }
54639         }
54640     },
54641
54642     handleHdOut : function(e){
54643         var hd = this.findHeaderCell(e.getTarget());
54644         if(hd){
54645             this.fly(hd).removeClass("x-grid-hd-over");
54646         }
54647     },
54648
54649     handleSplitDblClick : function(e, t){
54650         var i = this.getCellIndex(t);
54651         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54652             this.autoSizeColumn(i, true);
54653             this.layout();
54654         }
54655     },
54656
54657     render : function(){
54658
54659         var cm = this.cm;
54660         var colCount = cm.getColumnCount();
54661
54662         if(this.grid.monitorWindowResize === true){
54663             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54664         }
54665         var header = this.renderHeaders();
54666         var body = this.templates.body.apply({rows:""});
54667         var html = this.templates.master.apply({
54668             lockedBody: body,
54669             body: body,
54670             lockedHeader: header[0],
54671             header: header[1]
54672         });
54673
54674         //this.updateColumns();
54675
54676         this.grid.getGridEl().dom.innerHTML = html;
54677
54678         this.initElements();
54679         
54680         // a kludge to fix the random scolling effect in webkit
54681         this.el.on("scroll", function() {
54682             this.el.dom.scrollTop=0; // hopefully not recursive..
54683         },this);
54684
54685         this.scroller.on("scroll", this.handleScroll, this);
54686         this.lockedBody.on("mousewheel", this.handleWheel, this);
54687         this.mainBody.on("mousewheel", this.handleWheel, this);
54688
54689         this.mainHd.on("mouseover", this.handleHdOver, this);
54690         this.mainHd.on("mouseout", this.handleHdOut, this);
54691         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54692                 {delegate: "."+this.splitClass});
54693
54694         this.lockedHd.on("mouseover", this.handleHdOver, this);
54695         this.lockedHd.on("mouseout", this.handleHdOut, this);
54696         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54697                 {delegate: "."+this.splitClass});
54698
54699         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54700             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54701         }
54702
54703         this.updateSplitters();
54704
54705         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54706             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54707             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54708         }
54709
54710         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54711             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54712             this.hmenu.add(
54713                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54714                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54715             );
54716             if(this.grid.enableColLock !== false){
54717                 this.hmenu.add('-',
54718                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54719                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54720                 );
54721             }
54722             if (Roo.isTouch) {
54723                  this.hmenu.add('-',
54724                     {id:"wider", text: this.columnsWiderText},
54725                     {id:"narrow", text: this.columnsNarrowText }
54726                 );
54727                 
54728                  
54729             }
54730             
54731             if(this.grid.enableColumnHide !== false){
54732
54733                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54734                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54735                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54736
54737                 this.hmenu.add('-',
54738                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54739                 );
54740             }
54741             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54742
54743             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54744         }
54745
54746         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54747             this.dd = new Roo.grid.GridDragZone(this.grid, {
54748                 ddGroup : this.grid.ddGroup || 'GridDD'
54749             });
54750             
54751         }
54752
54753         /*
54754         for(var i = 0; i < colCount; i++){
54755             if(cm.isHidden(i)){
54756                 this.hideColumn(i);
54757             }
54758             if(cm.config[i].align){
54759                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54760                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54761             }
54762         }*/
54763         
54764         this.updateHeaderSortState();
54765
54766         this.beforeInitialResize();
54767         this.layout(true);
54768
54769         // two part rendering gives faster view to the user
54770         this.renderPhase2.defer(1, this);
54771     },
54772
54773     renderPhase2 : function(){
54774         // render the rows now
54775         this.refresh();
54776         if(this.grid.autoSizeColumns){
54777             this.autoSizeColumns();
54778         }
54779     },
54780
54781     beforeInitialResize : function(){
54782
54783     },
54784
54785     onColumnSplitterMoved : function(i, w){
54786         this.userResized = true;
54787         var cm = this.grid.colModel;
54788         cm.setColumnWidth(i, w, true);
54789         var cid = cm.getColumnId(i);
54790         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54791         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54792         this.updateSplitters();
54793         this.layout();
54794         this.grid.fireEvent("columnresize", i, w);
54795     },
54796
54797     syncRowHeights : function(startIndex, endIndex){
54798         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54799             startIndex = startIndex || 0;
54800             var mrows = this.getBodyTable().rows;
54801             var lrows = this.getLockedTable().rows;
54802             var len = mrows.length-1;
54803             endIndex = Math.min(endIndex || len, len);
54804             for(var i = startIndex; i <= endIndex; i++){
54805                 var m = mrows[i], l = lrows[i];
54806                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54807                 m.style.height = l.style.height = h + "px";
54808             }
54809         }
54810     },
54811
54812     layout : function(initialRender, is2ndPass){
54813         var g = this.grid;
54814         var auto = g.autoHeight;
54815         var scrollOffset = 16;
54816         var c = g.getGridEl(), cm = this.cm,
54817                 expandCol = g.autoExpandColumn,
54818                 gv = this;
54819         //c.beginMeasure();
54820
54821         if(!c.dom.offsetWidth){ // display:none?
54822             if(initialRender){
54823                 this.lockedWrap.show();
54824                 this.mainWrap.show();
54825             }
54826             return;
54827         }
54828
54829         var hasLock = this.cm.isLocked(0);
54830
54831         var tbh = this.headerPanel.getHeight();
54832         var bbh = this.footerPanel.getHeight();
54833
54834         if(auto){
54835             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54836             var newHeight = ch + c.getBorderWidth("tb");
54837             if(g.maxHeight){
54838                 newHeight = Math.min(g.maxHeight, newHeight);
54839             }
54840             c.setHeight(newHeight);
54841         }
54842
54843         if(g.autoWidth){
54844             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54845         }
54846
54847         var s = this.scroller;
54848
54849         var csize = c.getSize(true);
54850
54851         this.el.setSize(csize.width, csize.height);
54852
54853         this.headerPanel.setWidth(csize.width);
54854         this.footerPanel.setWidth(csize.width);
54855
54856         var hdHeight = this.mainHd.getHeight();
54857         var vw = csize.width;
54858         var vh = csize.height - (tbh + bbh);
54859
54860         s.setSize(vw, vh);
54861
54862         var bt = this.getBodyTable();
54863         var ltWidth = hasLock ?
54864                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54865
54866         var scrollHeight = bt.offsetHeight;
54867         var scrollWidth = ltWidth + bt.offsetWidth;
54868         var vscroll = false, hscroll = false;
54869
54870         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54871
54872         var lw = this.lockedWrap, mw = this.mainWrap;
54873         var lb = this.lockedBody, mb = this.mainBody;
54874
54875         setTimeout(function(){
54876             var t = s.dom.offsetTop;
54877             var w = s.dom.clientWidth,
54878                 h = s.dom.clientHeight;
54879
54880             lw.setTop(t);
54881             lw.setSize(ltWidth, h);
54882
54883             mw.setLeftTop(ltWidth, t);
54884             mw.setSize(w-ltWidth, h);
54885
54886             lb.setHeight(h-hdHeight);
54887             mb.setHeight(h-hdHeight);
54888
54889             if(is2ndPass !== true && !gv.userResized && expandCol){
54890                 // high speed resize without full column calculation
54891                 
54892                 var ci = cm.getIndexById(expandCol);
54893                 if (ci < 0) {
54894                     ci = cm.findColumnIndex(expandCol);
54895                 }
54896                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54897                 var expandId = cm.getColumnId(ci);
54898                 var  tw = cm.getTotalWidth(false);
54899                 var currentWidth = cm.getColumnWidth(ci);
54900                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54901                 if(currentWidth != cw){
54902                     cm.setColumnWidth(ci, cw, true);
54903                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54904                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54905                     gv.updateSplitters();
54906                     gv.layout(false, true);
54907                 }
54908             }
54909
54910             if(initialRender){
54911                 lw.show();
54912                 mw.show();
54913             }
54914             //c.endMeasure();
54915         }, 10);
54916     },
54917
54918     onWindowResize : function(){
54919         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54920             return;
54921         }
54922         this.layout();
54923     },
54924
54925     appendFooter : function(parentEl){
54926         return null;
54927     },
54928
54929     sortAscText : "Sort Ascending",
54930     sortDescText : "Sort Descending",
54931     lockText : "Lock Column",
54932     unlockText : "Unlock Column",
54933     columnsText : "Columns",
54934  
54935     columnsWiderText : "Wider",
54936     columnsNarrowText : "Thinner"
54937 });
54938
54939
54940 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54941     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54942     this.proxy.el.addClass('x-grid3-col-dd');
54943 };
54944
54945 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54946     handleMouseDown : function(e){
54947
54948     },
54949
54950     callHandleMouseDown : function(e){
54951         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54952     }
54953 });
54954 /*
54955  * Based on:
54956  * Ext JS Library 1.1.1
54957  * Copyright(c) 2006-2007, Ext JS, LLC.
54958  *
54959  * Originally Released Under LGPL - original licence link has changed is not relivant.
54960  *
54961  * Fork - LGPL
54962  * <script type="text/javascript">
54963  */
54964  
54965 // private
54966 // This is a support class used internally by the Grid components
54967 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54968     this.grid = grid;
54969     this.view = grid.getView();
54970     this.proxy = this.view.resizeProxy;
54971     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54972         "gridSplitters" + this.grid.getGridEl().id, {
54973         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54974     });
54975     this.setHandleElId(Roo.id(hd));
54976     this.setOuterHandleElId(Roo.id(hd2));
54977     this.scroll = false;
54978 };
54979 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54980     fly: Roo.Element.fly,
54981
54982     b4StartDrag : function(x, y){
54983         this.view.headersDisabled = true;
54984         this.proxy.setHeight(this.view.mainWrap.getHeight());
54985         var w = this.cm.getColumnWidth(this.cellIndex);
54986         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54987         this.resetConstraints();
54988         this.setXConstraint(minw, 1000);
54989         this.setYConstraint(0, 0);
54990         this.minX = x - minw;
54991         this.maxX = x + 1000;
54992         this.startPos = x;
54993         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54994     },
54995
54996
54997     handleMouseDown : function(e){
54998         ev = Roo.EventObject.setEvent(e);
54999         var t = this.fly(ev.getTarget());
55000         if(t.hasClass("x-grid-split")){
55001             this.cellIndex = this.view.getCellIndex(t.dom);
55002             this.split = t.dom;
55003             this.cm = this.grid.colModel;
55004             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55005                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55006             }
55007         }
55008     },
55009
55010     endDrag : function(e){
55011         this.view.headersDisabled = false;
55012         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55013         var diff = endX - this.startPos;
55014         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55015     },
55016
55017     autoOffset : function(){
55018         this.setDelta(0,0);
55019     }
55020 });/*
55021  * Based on:
55022  * Ext JS Library 1.1.1
55023  * Copyright(c) 2006-2007, Ext JS, LLC.
55024  *
55025  * Originally Released Under LGPL - original licence link has changed is not relivant.
55026  *
55027  * Fork - LGPL
55028  * <script type="text/javascript">
55029  */
55030  
55031 // private
55032 // This is a support class used internally by the Grid components
55033 Roo.grid.GridDragZone = function(grid, config){
55034     this.view = grid.getView();
55035     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55036     if(this.view.lockedBody){
55037         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55038         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55039     }
55040     this.scroll = false;
55041     this.grid = grid;
55042     this.ddel = document.createElement('div');
55043     this.ddel.className = 'x-grid-dd-wrap';
55044 };
55045
55046 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55047     ddGroup : "GridDD",
55048
55049     getDragData : function(e){
55050         var t = Roo.lib.Event.getTarget(e);
55051         var rowIndex = this.view.findRowIndex(t);
55052         var sm = this.grid.selModel;
55053             
55054         //Roo.log(rowIndex);
55055         
55056         if (sm.getSelectedCell) {
55057             // cell selection..
55058             if (!sm.getSelectedCell()) {
55059                 return false;
55060             }
55061             if (rowIndex != sm.getSelectedCell()[0]) {
55062                 return false;
55063             }
55064         
55065         }
55066         
55067         if(rowIndex !== false){
55068             
55069             // if editorgrid.. 
55070             
55071             
55072             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55073                
55074             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55075               //  
55076             //}
55077             if (e.hasModifier()){
55078                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55079             }
55080             
55081             Roo.log("getDragData");
55082             
55083             return {
55084                 grid: this.grid,
55085                 ddel: this.ddel,
55086                 rowIndex: rowIndex,
55087                 selections:sm.getSelections ? sm.getSelections() : (
55088                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55089                 )
55090             };
55091         }
55092         return false;
55093     },
55094
55095     onInitDrag : function(e){
55096         var data = this.dragData;
55097         this.ddel.innerHTML = this.grid.getDragDropText();
55098         this.proxy.update(this.ddel);
55099         // fire start drag?
55100     },
55101
55102     afterRepair : function(){
55103         this.dragging = false;
55104     },
55105
55106     getRepairXY : function(e, data){
55107         return false;
55108     },
55109
55110     onEndDrag : function(data, e){
55111         // fire end drag?
55112     },
55113
55114     onValidDrop : function(dd, e, id){
55115         // fire drag drop?
55116         this.hideProxy();
55117     },
55118
55119     beforeInvalidDrop : function(e, id){
55120
55121     }
55122 });/*
55123  * Based on:
55124  * Ext JS Library 1.1.1
55125  * Copyright(c) 2006-2007, Ext JS, LLC.
55126  *
55127  * Originally Released Under LGPL - original licence link has changed is not relivant.
55128  *
55129  * Fork - LGPL
55130  * <script type="text/javascript">
55131  */
55132  
55133
55134 /**
55135  * @class Roo.grid.ColumnModel
55136  * @extends Roo.util.Observable
55137  * This is the default implementation of a ColumnModel used by the Grid. It defines
55138  * the columns in the grid.
55139  * <br>Usage:<br>
55140  <pre><code>
55141  var colModel = new Roo.grid.ColumnModel([
55142         {header: "Ticker", width: 60, sortable: true, locked: true},
55143         {header: "Company Name", width: 150, sortable: true},
55144         {header: "Market Cap.", width: 100, sortable: true},
55145         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55146         {header: "Employees", width: 100, sortable: true, resizable: false}
55147  ]);
55148  </code></pre>
55149  * <p>
55150  
55151  * The config options listed for this class are options which may appear in each
55152  * individual column definition.
55153  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55154  * @constructor
55155  * @param {Object} config An Array of column config objects. See this class's
55156  * config objects for details.
55157 */
55158 Roo.grid.ColumnModel = function(config){
55159         /**
55160      * The config passed into the constructor
55161      */
55162     this.config = config;
55163     this.lookup = {};
55164
55165     // if no id, create one
55166     // if the column does not have a dataIndex mapping,
55167     // map it to the order it is in the config
55168     for(var i = 0, len = config.length; i < len; i++){
55169         var c = config[i];
55170         if(typeof c.dataIndex == "undefined"){
55171             c.dataIndex = i;
55172         }
55173         if(typeof c.renderer == "string"){
55174             c.renderer = Roo.util.Format[c.renderer];
55175         }
55176         if(typeof c.id == "undefined"){
55177             c.id = Roo.id();
55178         }
55179         if(c.editor && c.editor.xtype){
55180             c.editor  = Roo.factory(c.editor, Roo.grid);
55181         }
55182         if(c.editor && c.editor.isFormField){
55183             c.editor = new Roo.grid.GridEditor(c.editor);
55184         }
55185         this.lookup[c.id] = c;
55186     }
55187
55188     /**
55189      * The width of columns which have no width specified (defaults to 100)
55190      * @type Number
55191      */
55192     this.defaultWidth = 100;
55193
55194     /**
55195      * Default sortable of columns which have no sortable specified (defaults to false)
55196      * @type Boolean
55197      */
55198     this.defaultSortable = false;
55199
55200     this.addEvents({
55201         /**
55202              * @event widthchange
55203              * Fires when the width of a column changes.
55204              * @param {ColumnModel} this
55205              * @param {Number} columnIndex The column index
55206              * @param {Number} newWidth The new width
55207              */
55208             "widthchange": true,
55209         /**
55210              * @event headerchange
55211              * Fires when the text of a header changes.
55212              * @param {ColumnModel} this
55213              * @param {Number} columnIndex The column index
55214              * @param {Number} newText The new header text
55215              */
55216             "headerchange": true,
55217         /**
55218              * @event hiddenchange
55219              * Fires when a column is hidden or "unhidden".
55220              * @param {ColumnModel} this
55221              * @param {Number} columnIndex The column index
55222              * @param {Boolean} hidden true if hidden, false otherwise
55223              */
55224             "hiddenchange": true,
55225             /**
55226          * @event columnmoved
55227          * Fires when a column is moved.
55228          * @param {ColumnModel} this
55229          * @param {Number} oldIndex
55230          * @param {Number} newIndex
55231          */
55232         "columnmoved" : true,
55233         /**
55234          * @event columlockchange
55235          * Fires when a column's locked state is changed
55236          * @param {ColumnModel} this
55237          * @param {Number} colIndex
55238          * @param {Boolean} locked true if locked
55239          */
55240         "columnlockchange" : true
55241     });
55242     Roo.grid.ColumnModel.superclass.constructor.call(this);
55243 };
55244 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55245     /**
55246      * @cfg {String} header The header text to display in the Grid view.
55247      */
55248     /**
55249      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55250      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55251      * specified, the column's index is used as an index into the Record's data Array.
55252      */
55253     /**
55254      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55255      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55256      */
55257     /**
55258      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55259      * Defaults to the value of the {@link #defaultSortable} property.
55260      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55261      */
55262     /**
55263      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55264      */
55265     /**
55266      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55267      */
55268     /**
55269      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55270      */
55271     /**
55272      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55273      */
55274     /**
55275      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55276      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55277      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55278      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55279      */
55280        /**
55281      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55282      */
55283     /**
55284      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55285      */
55286     /**
55287      * @cfg {String} cursor (Optional)
55288      */
55289     /**
55290      * @cfg {String} tooltip (Optional)
55291      */
55292     /**
55293      * Returns the id of the column at the specified index.
55294      * @param {Number} index The column index
55295      * @return {String} the id
55296      */
55297     getColumnId : function(index){
55298         return this.config[index].id;
55299     },
55300
55301     /**
55302      * Returns the column for a specified id.
55303      * @param {String} id The column id
55304      * @return {Object} the column
55305      */
55306     getColumnById : function(id){
55307         return this.lookup[id];
55308     },
55309
55310     
55311     /**
55312      * Returns the column for a specified dataIndex.
55313      * @param {String} dataIndex The column dataIndex
55314      * @return {Object|Boolean} the column or false if not found
55315      */
55316     getColumnByDataIndex: function(dataIndex){
55317         var index = this.findColumnIndex(dataIndex);
55318         return index > -1 ? this.config[index] : false;
55319     },
55320     
55321     /**
55322      * Returns the index for a specified column id.
55323      * @param {String} id The column id
55324      * @return {Number} the index, or -1 if not found
55325      */
55326     getIndexById : function(id){
55327         for(var i = 0, len = this.config.length; i < len; i++){
55328             if(this.config[i].id == id){
55329                 return i;
55330             }
55331         }
55332         return -1;
55333     },
55334     
55335     /**
55336      * Returns the index for a specified column dataIndex.
55337      * @param {String} dataIndex The column dataIndex
55338      * @return {Number} the index, or -1 if not found
55339      */
55340     
55341     findColumnIndex : function(dataIndex){
55342         for(var i = 0, len = this.config.length; i < len; i++){
55343             if(this.config[i].dataIndex == dataIndex){
55344                 return i;
55345             }
55346         }
55347         return -1;
55348     },
55349     
55350     
55351     moveColumn : function(oldIndex, newIndex){
55352         var c = this.config[oldIndex];
55353         this.config.splice(oldIndex, 1);
55354         this.config.splice(newIndex, 0, c);
55355         this.dataMap = null;
55356         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55357     },
55358
55359     isLocked : function(colIndex){
55360         return this.config[colIndex].locked === true;
55361     },
55362
55363     setLocked : function(colIndex, value, suppressEvent){
55364         if(this.isLocked(colIndex) == value){
55365             return;
55366         }
55367         this.config[colIndex].locked = value;
55368         if(!suppressEvent){
55369             this.fireEvent("columnlockchange", this, colIndex, value);
55370         }
55371     },
55372
55373     getTotalLockedWidth : function(){
55374         var totalWidth = 0;
55375         for(var i = 0; i < this.config.length; i++){
55376             if(this.isLocked(i) && !this.isHidden(i)){
55377                 this.totalWidth += this.getColumnWidth(i);
55378             }
55379         }
55380         return totalWidth;
55381     },
55382
55383     getLockedCount : function(){
55384         for(var i = 0, len = this.config.length; i < len; i++){
55385             if(!this.isLocked(i)){
55386                 return i;
55387             }
55388         }
55389     },
55390
55391     /**
55392      * Returns the number of columns.
55393      * @return {Number}
55394      */
55395     getColumnCount : function(visibleOnly){
55396         if(visibleOnly === true){
55397             var c = 0;
55398             for(var i = 0, len = this.config.length; i < len; i++){
55399                 if(!this.isHidden(i)){
55400                     c++;
55401                 }
55402             }
55403             return c;
55404         }
55405         return this.config.length;
55406     },
55407
55408     /**
55409      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55410      * @param {Function} fn
55411      * @param {Object} scope (optional)
55412      * @return {Array} result
55413      */
55414     getColumnsBy : function(fn, scope){
55415         var r = [];
55416         for(var i = 0, len = this.config.length; i < len; i++){
55417             var c = this.config[i];
55418             if(fn.call(scope||this, c, i) === true){
55419                 r[r.length] = c;
55420             }
55421         }
55422         return r;
55423     },
55424
55425     /**
55426      * Returns true if the specified column is sortable.
55427      * @param {Number} col The column index
55428      * @return {Boolean}
55429      */
55430     isSortable : function(col){
55431         if(typeof this.config[col].sortable == "undefined"){
55432             return this.defaultSortable;
55433         }
55434         return this.config[col].sortable;
55435     },
55436
55437     /**
55438      * Returns the rendering (formatting) function defined for the column.
55439      * @param {Number} col The column index.
55440      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55441      */
55442     getRenderer : function(col){
55443         if(!this.config[col].renderer){
55444             return Roo.grid.ColumnModel.defaultRenderer;
55445         }
55446         return this.config[col].renderer;
55447     },
55448
55449     /**
55450      * Sets the rendering (formatting) function for a column.
55451      * @param {Number} col The column index
55452      * @param {Function} fn The function to use to process the cell's raw data
55453      * to return HTML markup for the grid view. The render function is called with
55454      * the following parameters:<ul>
55455      * <li>Data value.</li>
55456      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55457      * <li>css A CSS style string to apply to the table cell.</li>
55458      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55459      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55460      * <li>Row index</li>
55461      * <li>Column index</li>
55462      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55463      */
55464     setRenderer : function(col, fn){
55465         this.config[col].renderer = fn;
55466     },
55467
55468     /**
55469      * Returns the width for the specified column.
55470      * @param {Number} col The column index
55471      * @return {Number}
55472      */
55473     getColumnWidth : function(col){
55474         return this.config[col].width * 1 || this.defaultWidth;
55475     },
55476
55477     /**
55478      * Sets the width for a column.
55479      * @param {Number} col The column index
55480      * @param {Number} width The new width
55481      */
55482     setColumnWidth : function(col, width, suppressEvent){
55483         this.config[col].width = width;
55484         this.totalWidth = null;
55485         if(!suppressEvent){
55486              this.fireEvent("widthchange", this, col, width);
55487         }
55488     },
55489
55490     /**
55491      * Returns the total width of all columns.
55492      * @param {Boolean} includeHidden True to include hidden column widths
55493      * @return {Number}
55494      */
55495     getTotalWidth : function(includeHidden){
55496         if(!this.totalWidth){
55497             this.totalWidth = 0;
55498             for(var i = 0, len = this.config.length; i < len; i++){
55499                 if(includeHidden || !this.isHidden(i)){
55500                     this.totalWidth += this.getColumnWidth(i);
55501                 }
55502             }
55503         }
55504         return this.totalWidth;
55505     },
55506
55507     /**
55508      * Returns the header for the specified column.
55509      * @param {Number} col The column index
55510      * @return {String}
55511      */
55512     getColumnHeader : function(col){
55513         return this.config[col].header;
55514     },
55515
55516     /**
55517      * Sets the header for a column.
55518      * @param {Number} col The column index
55519      * @param {String} header The new header
55520      */
55521     setColumnHeader : function(col, header){
55522         this.config[col].header = header;
55523         this.fireEvent("headerchange", this, col, header);
55524     },
55525
55526     /**
55527      * Returns the tooltip for the specified column.
55528      * @param {Number} col The column index
55529      * @return {String}
55530      */
55531     getColumnTooltip : function(col){
55532             return this.config[col].tooltip;
55533     },
55534     /**
55535      * Sets the tooltip for a column.
55536      * @param {Number} col The column index
55537      * @param {String} tooltip The new tooltip
55538      */
55539     setColumnTooltip : function(col, tooltip){
55540             this.config[col].tooltip = tooltip;
55541     },
55542
55543     /**
55544      * Returns the dataIndex for the specified column.
55545      * @param {Number} col The column index
55546      * @return {Number}
55547      */
55548     getDataIndex : function(col){
55549         return this.config[col].dataIndex;
55550     },
55551
55552     /**
55553      * Sets the dataIndex for a column.
55554      * @param {Number} col The column index
55555      * @param {Number} dataIndex The new dataIndex
55556      */
55557     setDataIndex : function(col, dataIndex){
55558         this.config[col].dataIndex = dataIndex;
55559     },
55560
55561     
55562     
55563     /**
55564      * Returns true if the cell is editable.
55565      * @param {Number} colIndex The column index
55566      * @param {Number} rowIndex The row index
55567      * @return {Boolean}
55568      */
55569     isCellEditable : function(colIndex, rowIndex){
55570         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55571     },
55572
55573     /**
55574      * Returns the editor defined for the cell/column.
55575      * return false or null to disable editing.
55576      * @param {Number} colIndex The column index
55577      * @param {Number} rowIndex The row index
55578      * @return {Object}
55579      */
55580     getCellEditor : function(colIndex, rowIndex){
55581         return this.config[colIndex].editor;
55582     },
55583
55584     /**
55585      * Sets if a column is editable.
55586      * @param {Number} col The column index
55587      * @param {Boolean} editable True if the column is editable
55588      */
55589     setEditable : function(col, editable){
55590         this.config[col].editable = editable;
55591     },
55592
55593
55594     /**
55595      * Returns true if the column is hidden.
55596      * @param {Number} colIndex The column index
55597      * @return {Boolean}
55598      */
55599     isHidden : function(colIndex){
55600         return this.config[colIndex].hidden;
55601     },
55602
55603
55604     /**
55605      * Returns true if the column width cannot be changed
55606      */
55607     isFixed : function(colIndex){
55608         return this.config[colIndex].fixed;
55609     },
55610
55611     /**
55612      * Returns true if the column can be resized
55613      * @return {Boolean}
55614      */
55615     isResizable : function(colIndex){
55616         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55617     },
55618     /**
55619      * Sets if a column is hidden.
55620      * @param {Number} colIndex The column index
55621      * @param {Boolean} hidden True if the column is hidden
55622      */
55623     setHidden : function(colIndex, hidden){
55624         this.config[colIndex].hidden = hidden;
55625         this.totalWidth = null;
55626         this.fireEvent("hiddenchange", this, colIndex, hidden);
55627     },
55628
55629     /**
55630      * Sets the editor for a column.
55631      * @param {Number} col The column index
55632      * @param {Object} editor The editor object
55633      */
55634     setEditor : function(col, editor){
55635         this.config[col].editor = editor;
55636     }
55637 });
55638
55639 Roo.grid.ColumnModel.defaultRenderer = function(value){
55640         if(typeof value == "string" && value.length < 1){
55641             return "&#160;";
55642         }
55643         return value;
55644 };
55645
55646 // Alias for backwards compatibility
55647 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55648 /*
55649  * Based on:
55650  * Ext JS Library 1.1.1
55651  * Copyright(c) 2006-2007, Ext JS, LLC.
55652  *
55653  * Originally Released Under LGPL - original licence link has changed is not relivant.
55654  *
55655  * Fork - LGPL
55656  * <script type="text/javascript">
55657  */
55658
55659 /**
55660  * @class Roo.grid.AbstractSelectionModel
55661  * @extends Roo.util.Observable
55662  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55663  * implemented by descendant classes.  This class should not be directly instantiated.
55664  * @constructor
55665  */
55666 Roo.grid.AbstractSelectionModel = function(){
55667     this.locked = false;
55668     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55669 };
55670
55671 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55672     /** @ignore Called by the grid automatically. Do not call directly. */
55673     init : function(grid){
55674         this.grid = grid;
55675         this.initEvents();
55676     },
55677
55678     /**
55679      * Locks the selections.
55680      */
55681     lock : function(){
55682         this.locked = true;
55683     },
55684
55685     /**
55686      * Unlocks the selections.
55687      */
55688     unlock : function(){
55689         this.locked = false;
55690     },
55691
55692     /**
55693      * Returns true if the selections are locked.
55694      * @return {Boolean}
55695      */
55696     isLocked : function(){
55697         return this.locked;
55698     }
55699 });/*
55700  * Based on:
55701  * Ext JS Library 1.1.1
55702  * Copyright(c) 2006-2007, Ext JS, LLC.
55703  *
55704  * Originally Released Under LGPL - original licence link has changed is not relivant.
55705  *
55706  * Fork - LGPL
55707  * <script type="text/javascript">
55708  */
55709 /**
55710  * @extends Roo.grid.AbstractSelectionModel
55711  * @class Roo.grid.RowSelectionModel
55712  * The default SelectionModel used by {@link Roo.grid.Grid}.
55713  * It supports multiple selections and keyboard selection/navigation. 
55714  * @constructor
55715  * @param {Object} config
55716  */
55717 Roo.grid.RowSelectionModel = function(config){
55718     Roo.apply(this, config);
55719     this.selections = new Roo.util.MixedCollection(false, function(o){
55720         return o.id;
55721     });
55722
55723     this.last = false;
55724     this.lastActive = false;
55725
55726     this.addEvents({
55727         /**
55728              * @event selectionchange
55729              * Fires when the selection changes
55730              * @param {SelectionModel} this
55731              */
55732             "selectionchange" : true,
55733         /**
55734              * @event afterselectionchange
55735              * Fires after the selection changes (eg. by key press or clicking)
55736              * @param {SelectionModel} this
55737              */
55738             "afterselectionchange" : true,
55739         /**
55740              * @event beforerowselect
55741              * Fires when a row is selected being selected, return false to cancel.
55742              * @param {SelectionModel} this
55743              * @param {Number} rowIndex The selected index
55744              * @param {Boolean} keepExisting False if other selections will be cleared
55745              */
55746             "beforerowselect" : true,
55747         /**
55748              * @event rowselect
55749              * Fires when a row is selected.
55750              * @param {SelectionModel} this
55751              * @param {Number} rowIndex The selected index
55752              * @param {Roo.data.Record} r The record
55753              */
55754             "rowselect" : true,
55755         /**
55756              * @event rowdeselect
55757              * Fires when a row is deselected.
55758              * @param {SelectionModel} this
55759              * @param {Number} rowIndex The selected index
55760              */
55761         "rowdeselect" : true
55762     });
55763     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55764     this.locked = false;
55765 };
55766
55767 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55768     /**
55769      * @cfg {Boolean} singleSelect
55770      * True to allow selection of only one row at a time (defaults to false)
55771      */
55772     singleSelect : false,
55773
55774     // private
55775     initEvents : function(){
55776
55777         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55778             this.grid.on("mousedown", this.handleMouseDown, this);
55779         }else{ // allow click to work like normal
55780             this.grid.on("rowclick", this.handleDragableRowClick, this);
55781         }
55782
55783         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55784             "up" : function(e){
55785                 if(!e.shiftKey){
55786                     this.selectPrevious(e.shiftKey);
55787                 }else if(this.last !== false && this.lastActive !== false){
55788                     var last = this.last;
55789                     this.selectRange(this.last,  this.lastActive-1);
55790                     this.grid.getView().focusRow(this.lastActive);
55791                     if(last !== false){
55792                         this.last = last;
55793                     }
55794                 }else{
55795                     this.selectFirstRow();
55796                 }
55797                 this.fireEvent("afterselectionchange", this);
55798             },
55799             "down" : function(e){
55800                 if(!e.shiftKey){
55801                     this.selectNext(e.shiftKey);
55802                 }else if(this.last !== false && this.lastActive !== false){
55803                     var last = this.last;
55804                     this.selectRange(this.last,  this.lastActive+1);
55805                     this.grid.getView().focusRow(this.lastActive);
55806                     if(last !== false){
55807                         this.last = last;
55808                     }
55809                 }else{
55810                     this.selectFirstRow();
55811                 }
55812                 this.fireEvent("afterselectionchange", this);
55813             },
55814             scope: this
55815         });
55816
55817         var view = this.grid.view;
55818         view.on("refresh", this.onRefresh, this);
55819         view.on("rowupdated", this.onRowUpdated, this);
55820         view.on("rowremoved", this.onRemove, this);
55821     },
55822
55823     // private
55824     onRefresh : function(){
55825         var ds = this.grid.dataSource, i, v = this.grid.view;
55826         var s = this.selections;
55827         s.each(function(r){
55828             if((i = ds.indexOfId(r.id)) != -1){
55829                 v.onRowSelect(i);
55830             }else{
55831                 s.remove(r);
55832             }
55833         });
55834     },
55835
55836     // private
55837     onRemove : function(v, index, r){
55838         this.selections.remove(r);
55839     },
55840
55841     // private
55842     onRowUpdated : function(v, index, r){
55843         if(this.isSelected(r)){
55844             v.onRowSelect(index);
55845         }
55846     },
55847
55848     /**
55849      * Select records.
55850      * @param {Array} records The records to select
55851      * @param {Boolean} keepExisting (optional) True to keep existing selections
55852      */
55853     selectRecords : function(records, keepExisting){
55854         if(!keepExisting){
55855             this.clearSelections();
55856         }
55857         var ds = this.grid.dataSource;
55858         for(var i = 0, len = records.length; i < len; i++){
55859             this.selectRow(ds.indexOf(records[i]), true);
55860         }
55861     },
55862
55863     /**
55864      * Gets the number of selected rows.
55865      * @return {Number}
55866      */
55867     getCount : function(){
55868         return this.selections.length;
55869     },
55870
55871     /**
55872      * Selects the first row in the grid.
55873      */
55874     selectFirstRow : function(){
55875         this.selectRow(0);
55876     },
55877
55878     /**
55879      * Select the last row.
55880      * @param {Boolean} keepExisting (optional) True to keep existing selections
55881      */
55882     selectLastRow : function(keepExisting){
55883         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55884     },
55885
55886     /**
55887      * Selects the row immediately following the last selected row.
55888      * @param {Boolean} keepExisting (optional) True to keep existing selections
55889      */
55890     selectNext : function(keepExisting){
55891         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55892             this.selectRow(this.last+1, keepExisting);
55893             this.grid.getView().focusRow(this.last);
55894         }
55895     },
55896
55897     /**
55898      * Selects the row that precedes the last selected row.
55899      * @param {Boolean} keepExisting (optional) True to keep existing selections
55900      */
55901     selectPrevious : function(keepExisting){
55902         if(this.last){
55903             this.selectRow(this.last-1, keepExisting);
55904             this.grid.getView().focusRow(this.last);
55905         }
55906     },
55907
55908     /**
55909      * Returns the selected records
55910      * @return {Array} Array of selected records
55911      */
55912     getSelections : function(){
55913         return [].concat(this.selections.items);
55914     },
55915
55916     /**
55917      * Returns the first selected record.
55918      * @return {Record}
55919      */
55920     getSelected : function(){
55921         return this.selections.itemAt(0);
55922     },
55923
55924
55925     /**
55926      * Clears all selections.
55927      */
55928     clearSelections : function(fast){
55929         if(this.locked) return;
55930         if(fast !== true){
55931             var ds = this.grid.dataSource;
55932             var s = this.selections;
55933             s.each(function(r){
55934                 this.deselectRow(ds.indexOfId(r.id));
55935             }, this);
55936             s.clear();
55937         }else{
55938             this.selections.clear();
55939         }
55940         this.last = false;
55941     },
55942
55943
55944     /**
55945      * Selects all rows.
55946      */
55947     selectAll : function(){
55948         if(this.locked) return;
55949         this.selections.clear();
55950         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55951             this.selectRow(i, true);
55952         }
55953     },
55954
55955     /**
55956      * Returns True if there is a selection.
55957      * @return {Boolean}
55958      */
55959     hasSelection : function(){
55960         return this.selections.length > 0;
55961     },
55962
55963     /**
55964      * Returns True if the specified row is selected.
55965      * @param {Number/Record} record The record or index of the record to check
55966      * @return {Boolean}
55967      */
55968     isSelected : function(index){
55969         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55970         return (r && this.selections.key(r.id) ? true : false);
55971     },
55972
55973     /**
55974      * Returns True if the specified record id is selected.
55975      * @param {String} id The id of record to check
55976      * @return {Boolean}
55977      */
55978     isIdSelected : function(id){
55979         return (this.selections.key(id) ? true : false);
55980     },
55981
55982     // private
55983     handleMouseDown : function(e, t){
55984         var view = this.grid.getView(), rowIndex;
55985         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55986             return;
55987         };
55988         if(e.shiftKey && this.last !== false){
55989             var last = this.last;
55990             this.selectRange(last, rowIndex, e.ctrlKey);
55991             this.last = last; // reset the last
55992             view.focusRow(rowIndex);
55993         }else{
55994             var isSelected = this.isSelected(rowIndex);
55995             if(e.button !== 0 && isSelected){
55996                 view.focusRow(rowIndex);
55997             }else if(e.ctrlKey && isSelected){
55998                 this.deselectRow(rowIndex);
55999             }else if(!isSelected){
56000                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56001                 view.focusRow(rowIndex);
56002             }
56003         }
56004         this.fireEvent("afterselectionchange", this);
56005     },
56006     // private
56007     handleDragableRowClick :  function(grid, rowIndex, e) 
56008     {
56009         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56010             this.selectRow(rowIndex, false);
56011             grid.view.focusRow(rowIndex);
56012              this.fireEvent("afterselectionchange", this);
56013         }
56014     },
56015     
56016     /**
56017      * Selects multiple rows.
56018      * @param {Array} rows Array of the indexes of the row to select
56019      * @param {Boolean} keepExisting (optional) True to keep existing selections
56020      */
56021     selectRows : function(rows, keepExisting){
56022         if(!keepExisting){
56023             this.clearSelections();
56024         }
56025         for(var i = 0, len = rows.length; i < len; i++){
56026             this.selectRow(rows[i], true);
56027         }
56028     },
56029
56030     /**
56031      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56032      * @param {Number} startRow The index of the first row in the range
56033      * @param {Number} endRow The index of the last row in the range
56034      * @param {Boolean} keepExisting (optional) True to retain existing selections
56035      */
56036     selectRange : function(startRow, endRow, keepExisting){
56037         if(this.locked) return;
56038         if(!keepExisting){
56039             this.clearSelections();
56040         }
56041         if(startRow <= endRow){
56042             for(var i = startRow; i <= endRow; i++){
56043                 this.selectRow(i, true);
56044             }
56045         }else{
56046             for(var i = startRow; i >= endRow; i--){
56047                 this.selectRow(i, true);
56048             }
56049         }
56050     },
56051
56052     /**
56053      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56054      * @param {Number} startRow The index of the first row in the range
56055      * @param {Number} endRow The index of the last row in the range
56056      */
56057     deselectRange : function(startRow, endRow, preventViewNotify){
56058         if(this.locked) return;
56059         for(var i = startRow; i <= endRow; i++){
56060             this.deselectRow(i, preventViewNotify);
56061         }
56062     },
56063
56064     /**
56065      * Selects a row.
56066      * @param {Number} row The index of the row to select
56067      * @param {Boolean} keepExisting (optional) True to keep existing selections
56068      */
56069     selectRow : function(index, keepExisting, preventViewNotify){
56070         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56071         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56072             if(!keepExisting || this.singleSelect){
56073                 this.clearSelections();
56074             }
56075             var r = this.grid.dataSource.getAt(index);
56076             this.selections.add(r);
56077             this.last = this.lastActive = index;
56078             if(!preventViewNotify){
56079                 this.grid.getView().onRowSelect(index);
56080             }
56081             this.fireEvent("rowselect", this, index, r);
56082             this.fireEvent("selectionchange", this);
56083         }
56084     },
56085
56086     /**
56087      * Deselects a row.
56088      * @param {Number} row The index of the row to deselect
56089      */
56090     deselectRow : function(index, preventViewNotify){
56091         if(this.locked) return;
56092         if(this.last == index){
56093             this.last = false;
56094         }
56095         if(this.lastActive == index){
56096             this.lastActive = false;
56097         }
56098         var r = this.grid.dataSource.getAt(index);
56099         this.selections.remove(r);
56100         if(!preventViewNotify){
56101             this.grid.getView().onRowDeselect(index);
56102         }
56103         this.fireEvent("rowdeselect", this, index);
56104         this.fireEvent("selectionchange", this);
56105     },
56106
56107     // private
56108     restoreLast : function(){
56109         if(this._last){
56110             this.last = this._last;
56111         }
56112     },
56113
56114     // private
56115     acceptsNav : function(row, col, cm){
56116         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56117     },
56118
56119     // private
56120     onEditorKey : function(field, e){
56121         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56122         if(k == e.TAB){
56123             e.stopEvent();
56124             ed.completeEdit();
56125             if(e.shiftKey){
56126                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56127             }else{
56128                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56129             }
56130         }else if(k == e.ENTER && !e.ctrlKey){
56131             e.stopEvent();
56132             ed.completeEdit();
56133             if(e.shiftKey){
56134                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56135             }else{
56136                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56137             }
56138         }else if(k == e.ESC){
56139             ed.cancelEdit();
56140         }
56141         if(newCell){
56142             g.startEditing(newCell[0], newCell[1]);
56143         }
56144     }
56145 });/*
56146  * Based on:
56147  * Ext JS Library 1.1.1
56148  * Copyright(c) 2006-2007, Ext JS, LLC.
56149  *
56150  * Originally Released Under LGPL - original licence link has changed is not relivant.
56151  *
56152  * Fork - LGPL
56153  * <script type="text/javascript">
56154  */
56155 /**
56156  * @class Roo.grid.CellSelectionModel
56157  * @extends Roo.grid.AbstractSelectionModel
56158  * This class provides the basic implementation for cell selection in a grid.
56159  * @constructor
56160  * @param {Object} config The object containing the configuration of this model.
56161  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56162  */
56163 Roo.grid.CellSelectionModel = function(config){
56164     Roo.apply(this, config);
56165
56166     this.selection = null;
56167
56168     this.addEvents({
56169         /**
56170              * @event beforerowselect
56171              * Fires before a cell is selected.
56172              * @param {SelectionModel} this
56173              * @param {Number} rowIndex The selected row index
56174              * @param {Number} colIndex The selected cell index
56175              */
56176             "beforecellselect" : true,
56177         /**
56178              * @event cellselect
56179              * Fires when a cell is selected.
56180              * @param {SelectionModel} this
56181              * @param {Number} rowIndex The selected row index
56182              * @param {Number} colIndex The selected cell index
56183              */
56184             "cellselect" : true,
56185         /**
56186              * @event selectionchange
56187              * Fires when the active selection changes.
56188              * @param {SelectionModel} this
56189              * @param {Object} selection null for no selection or an object (o) with two properties
56190                 <ul>
56191                 <li>o.record: the record object for the row the selection is in</li>
56192                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56193                 </ul>
56194              */
56195             "selectionchange" : true,
56196         /**
56197              * @event tabend
56198              * Fires when the tab (or enter) was pressed on the last editable cell
56199              * You can use this to trigger add new row.
56200              * @param {SelectionModel} this
56201              */
56202             "tabend" : true,
56203          /**
56204              * @event beforeeditnext
56205              * Fires before the next editable sell is made active
56206              * You can use this to skip to another cell or fire the tabend
56207              *    if you set cell to false
56208              * @param {Object} eventdata object : { cell : [ row, col ] } 
56209              */
56210             "beforeeditnext" : true
56211     });
56212     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56213 };
56214
56215 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56216     
56217     enter_is_tab: false,
56218
56219     /** @ignore */
56220     initEvents : function(){
56221         this.grid.on("mousedown", this.handleMouseDown, this);
56222         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56223         var view = this.grid.view;
56224         view.on("refresh", this.onViewChange, this);
56225         view.on("rowupdated", this.onRowUpdated, this);
56226         view.on("beforerowremoved", this.clearSelections, this);
56227         view.on("beforerowsinserted", this.clearSelections, this);
56228         if(this.grid.isEditor){
56229             this.grid.on("beforeedit", this.beforeEdit,  this);
56230         }
56231     },
56232
56233         //private
56234     beforeEdit : function(e){
56235         this.select(e.row, e.column, false, true, e.record);
56236     },
56237
56238         //private
56239     onRowUpdated : function(v, index, r){
56240         if(this.selection && this.selection.record == r){
56241             v.onCellSelect(index, this.selection.cell[1]);
56242         }
56243     },
56244
56245         //private
56246     onViewChange : function(){
56247         this.clearSelections(true);
56248     },
56249
56250         /**
56251          * Returns the currently selected cell,.
56252          * @return {Array} The selected cell (row, column) or null if none selected.
56253          */
56254     getSelectedCell : function(){
56255         return this.selection ? this.selection.cell : null;
56256     },
56257
56258     /**
56259      * Clears all selections.
56260      * @param {Boolean} true to prevent the gridview from being notified about the change.
56261      */
56262     clearSelections : function(preventNotify){
56263         var s = this.selection;
56264         if(s){
56265             if(preventNotify !== true){
56266                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56267             }
56268             this.selection = null;
56269             this.fireEvent("selectionchange", this, null);
56270         }
56271     },
56272
56273     /**
56274      * Returns true if there is a selection.
56275      * @return {Boolean}
56276      */
56277     hasSelection : function(){
56278         return this.selection ? true : false;
56279     },
56280
56281     /** @ignore */
56282     handleMouseDown : function(e, t){
56283         var v = this.grid.getView();
56284         if(this.isLocked()){
56285             return;
56286         };
56287         var row = v.findRowIndex(t);
56288         var cell = v.findCellIndex(t);
56289         if(row !== false && cell !== false){
56290             this.select(row, cell);
56291         }
56292     },
56293
56294     /**
56295      * Selects a cell.
56296      * @param {Number} rowIndex
56297      * @param {Number} collIndex
56298      */
56299     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56300         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56301             this.clearSelections();
56302             r = r || this.grid.dataSource.getAt(rowIndex);
56303             this.selection = {
56304                 record : r,
56305                 cell : [rowIndex, colIndex]
56306             };
56307             if(!preventViewNotify){
56308                 var v = this.grid.getView();
56309                 v.onCellSelect(rowIndex, colIndex);
56310                 if(preventFocus !== true){
56311                     v.focusCell(rowIndex, colIndex);
56312                 }
56313             }
56314             this.fireEvent("cellselect", this, rowIndex, colIndex);
56315             this.fireEvent("selectionchange", this, this.selection);
56316         }
56317     },
56318
56319         //private
56320     isSelectable : function(rowIndex, colIndex, cm){
56321         return !cm.isHidden(colIndex);
56322     },
56323
56324     /** @ignore */
56325     handleKeyDown : function(e){
56326         //Roo.log('Cell Sel Model handleKeyDown');
56327         if(!e.isNavKeyPress()){
56328             return;
56329         }
56330         var g = this.grid, s = this.selection;
56331         if(!s){
56332             e.stopEvent();
56333             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56334             if(cell){
56335                 this.select(cell[0], cell[1]);
56336             }
56337             return;
56338         }
56339         var sm = this;
56340         var walk = function(row, col, step){
56341             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56342         };
56343         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56344         var newCell;
56345
56346       
56347
56348         switch(k){
56349             case e.TAB:
56350                 // handled by onEditorKey
56351                 if (g.isEditor && g.editing) {
56352                     return;
56353                 }
56354                 if(e.shiftKey) {
56355                     newCell = walk(r, c-1, -1);
56356                 } else {
56357                     newCell = walk(r, c+1, 1);
56358                 }
56359                 break;
56360             
56361             case e.DOWN:
56362                newCell = walk(r+1, c, 1);
56363                 break;
56364             
56365             case e.UP:
56366                 newCell = walk(r-1, c, -1);
56367                 break;
56368             
56369             case e.RIGHT:
56370                 newCell = walk(r, c+1, 1);
56371                 break;
56372             
56373             case e.LEFT:
56374                 newCell = walk(r, c-1, -1);
56375                 break;
56376             
56377             case e.ENTER:
56378                 
56379                 if(g.isEditor && !g.editing){
56380                    g.startEditing(r, c);
56381                    e.stopEvent();
56382                    return;
56383                 }
56384                 
56385                 
56386              break;
56387         };
56388         if(newCell){
56389             this.select(newCell[0], newCell[1]);
56390             e.stopEvent();
56391             
56392         }
56393     },
56394
56395     acceptsNav : function(row, col, cm){
56396         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56397     },
56398     /**
56399      * Selects a cell.
56400      * @param {Number} field (not used) - as it's normally used as a listener
56401      * @param {Number} e - event - fake it by using
56402      *
56403      * var e = Roo.EventObjectImpl.prototype;
56404      * e.keyCode = e.TAB
56405      *
56406      * 
56407      */
56408     onEditorKey : function(field, e){
56409         
56410         var k = e.getKey(),
56411             newCell,
56412             g = this.grid,
56413             ed = g.activeEditor,
56414             forward = false;
56415         ///Roo.log('onEditorKey' + k);
56416         
56417         
56418         if (this.enter_is_tab && k == e.ENTER) {
56419             k = e.TAB;
56420         }
56421         
56422         if(k == e.TAB){
56423             if(e.shiftKey){
56424                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56425             }else{
56426                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56427                 forward = true;
56428             }
56429             
56430             e.stopEvent();
56431             
56432         } else if(k == e.ENTER &&  !e.ctrlKey){
56433             ed.completeEdit();
56434             e.stopEvent();
56435             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56436         
56437                 } else if(k == e.ESC){
56438             ed.cancelEdit();
56439         }
56440                 
56441         if (newCell) {
56442             var ecall = { cell : newCell, forward : forward };
56443             this.fireEvent('beforeeditnext', ecall );
56444             newCell = ecall.cell;
56445                         forward = ecall.forward;
56446         }
56447                 
56448         if(newCell){
56449             //Roo.log('next cell after edit');
56450             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56451         } else if (forward) {
56452             // tabbed past last
56453             this.fireEvent.defer(100, this, ['tabend',this]);
56454         }
56455     }
56456 });/*
56457  * Based on:
56458  * Ext JS Library 1.1.1
56459  * Copyright(c) 2006-2007, Ext JS, LLC.
56460  *
56461  * Originally Released Under LGPL - original licence link has changed is not relivant.
56462  *
56463  * Fork - LGPL
56464  * <script type="text/javascript">
56465  */
56466  
56467 /**
56468  * @class Roo.grid.EditorGrid
56469  * @extends Roo.grid.Grid
56470  * Class for creating and editable grid.
56471  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56472  * The container MUST have some type of size defined for the grid to fill. The container will be 
56473  * automatically set to position relative if it isn't already.
56474  * @param {Object} dataSource The data model to bind to
56475  * @param {Object} colModel The column model with info about this grid's columns
56476  */
56477 Roo.grid.EditorGrid = function(container, config){
56478     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56479     this.getGridEl().addClass("xedit-grid");
56480
56481     if(!this.selModel){
56482         this.selModel = new Roo.grid.CellSelectionModel();
56483     }
56484
56485     this.activeEditor = null;
56486
56487         this.addEvents({
56488             /**
56489              * @event beforeedit
56490              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56491              * <ul style="padding:5px;padding-left:16px;">
56492              * <li>grid - This grid</li>
56493              * <li>record - The record being edited</li>
56494              * <li>field - The field name being edited</li>
56495              * <li>value - The value for the field being edited.</li>
56496              * <li>row - The grid row index</li>
56497              * <li>column - The grid column index</li>
56498              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56499              * </ul>
56500              * @param {Object} e An edit event (see above for description)
56501              */
56502             "beforeedit" : true,
56503             /**
56504              * @event afteredit
56505              * Fires after a cell is edited. <br />
56506              * <ul style="padding:5px;padding-left:16px;">
56507              * <li>grid - This grid</li>
56508              * <li>record - The record being edited</li>
56509              * <li>field - The field name being edited</li>
56510              * <li>value - The value being set</li>
56511              * <li>originalValue - The original value for the field, before the edit.</li>
56512              * <li>row - The grid row index</li>
56513              * <li>column - The grid column index</li>
56514              * </ul>
56515              * @param {Object} e An edit event (see above for description)
56516              */
56517             "afteredit" : true,
56518             /**
56519              * @event validateedit
56520              * Fires after a cell is edited, but before the value is set in the record. 
56521          * You can use this to modify the value being set in the field, Return false
56522              * to cancel the change. The edit event object has the following properties <br />
56523              * <ul style="padding:5px;padding-left:16px;">
56524          * <li>editor - This editor</li>
56525              * <li>grid - This grid</li>
56526              * <li>record - The record being edited</li>
56527              * <li>field - The field name being edited</li>
56528              * <li>value - The value being set</li>
56529              * <li>originalValue - The original value for the field, before the edit.</li>
56530              * <li>row - The grid row index</li>
56531              * <li>column - The grid column index</li>
56532              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56533              * </ul>
56534              * @param {Object} e An edit event (see above for description)
56535              */
56536             "validateedit" : true
56537         });
56538     this.on("bodyscroll", this.stopEditing,  this);
56539     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56540 };
56541
56542 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56543     /**
56544      * @cfg {Number} clicksToEdit
56545      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56546      */
56547     clicksToEdit: 2,
56548
56549     // private
56550     isEditor : true,
56551     // private
56552     trackMouseOver: false, // causes very odd FF errors
56553
56554     onCellDblClick : function(g, row, col){
56555         this.startEditing(row, col);
56556     },
56557
56558     onEditComplete : function(ed, value, startValue){
56559         this.editing = false;
56560         this.activeEditor = null;
56561         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56562         var r = ed.record;
56563         var field = this.colModel.getDataIndex(ed.col);
56564         var e = {
56565             grid: this,
56566             record: r,
56567             field: field,
56568             originalValue: startValue,
56569             value: value,
56570             row: ed.row,
56571             column: ed.col,
56572             cancel:false,
56573             editor: ed
56574         };
56575         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56576         cell.show();
56577           
56578         if(String(value) !== String(startValue)){
56579             
56580             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56581                 r.set(field, e.value);
56582                 // if we are dealing with a combo box..
56583                 // then we also set the 'name' colum to be the displayField
56584                 if (ed.field.displayField && ed.field.name) {
56585                     r.set(ed.field.name, ed.field.el.dom.value);
56586                 }
56587                 
56588                 delete e.cancel; //?? why!!!
56589                 this.fireEvent("afteredit", e);
56590             }
56591         } else {
56592             this.fireEvent("afteredit", e); // always fire it!
56593         }
56594         this.view.focusCell(ed.row, ed.col);
56595     },
56596
56597     /**
56598      * Starts editing the specified for the specified row/column
56599      * @param {Number} rowIndex
56600      * @param {Number} colIndex
56601      */
56602     startEditing : function(row, col){
56603         this.stopEditing();
56604         if(this.colModel.isCellEditable(col, row)){
56605             this.view.ensureVisible(row, col, true);
56606           
56607             var r = this.dataSource.getAt(row);
56608             var field = this.colModel.getDataIndex(col);
56609             var cell = Roo.get(this.view.getCell(row,col));
56610             var e = {
56611                 grid: this,
56612                 record: r,
56613                 field: field,
56614                 value: r.data[field],
56615                 row: row,
56616                 column: col,
56617                 cancel:false 
56618             };
56619             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56620                 this.editing = true;
56621                 var ed = this.colModel.getCellEditor(col, row);
56622                 
56623                 if (!ed) {
56624                     return;
56625                 }
56626                 if(!ed.rendered){
56627                     ed.render(ed.parentEl || document.body);
56628                 }
56629                 ed.field.reset();
56630                
56631                 cell.hide();
56632                 
56633                 (function(){ // complex but required for focus issues in safari, ie and opera
56634                     ed.row = row;
56635                     ed.col = col;
56636                     ed.record = r;
56637                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56638                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56639                     this.activeEditor = ed;
56640                     var v = r.data[field];
56641                     ed.startEdit(this.view.getCell(row, col), v);
56642                     // combo's with 'displayField and name set
56643                     if (ed.field.displayField && ed.field.name) {
56644                         ed.field.el.dom.value = r.data[ed.field.name];
56645                     }
56646                     
56647                     
56648                 }).defer(50, this);
56649             }
56650         }
56651     },
56652         
56653     /**
56654      * Stops any active editing
56655      */
56656     stopEditing : function(){
56657         if(this.activeEditor){
56658             this.activeEditor.completeEdit();
56659         }
56660         this.activeEditor = null;
56661     },
56662         
56663          /**
56664      * Called to get grid's drag proxy text, by default returns this.ddText.
56665      * @return {String}
56666      */
56667     getDragDropText : function(){
56668         var count = this.selModel.getSelectedCell() ? 1 : 0;
56669         return String.format(this.ddText, count, count == 1 ? '' : 's');
56670     }
56671         
56672 });/*
56673  * Based on:
56674  * Ext JS Library 1.1.1
56675  * Copyright(c) 2006-2007, Ext JS, LLC.
56676  *
56677  * Originally Released Under LGPL - original licence link has changed is not relivant.
56678  *
56679  * Fork - LGPL
56680  * <script type="text/javascript">
56681  */
56682
56683 // private - not really -- you end up using it !
56684 // This is a support class used internally by the Grid components
56685
56686 /**
56687  * @class Roo.grid.GridEditor
56688  * @extends Roo.Editor
56689  * Class for creating and editable grid elements.
56690  * @param {Object} config any settings (must include field)
56691  */
56692 Roo.grid.GridEditor = function(field, config){
56693     if (!config && field.field) {
56694         config = field;
56695         field = Roo.factory(config.field, Roo.form);
56696     }
56697     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56698     field.monitorTab = false;
56699 };
56700
56701 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56702     
56703     /**
56704      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56705      */
56706     
56707     alignment: "tl-tl",
56708     autoSize: "width",
56709     hideEl : false,
56710     cls: "x-small-editor x-grid-editor",
56711     shim:false,
56712     shadow:"frame"
56713 });/*
56714  * Based on:
56715  * Ext JS Library 1.1.1
56716  * Copyright(c) 2006-2007, Ext JS, LLC.
56717  *
56718  * Originally Released Under LGPL - original licence link has changed is not relivant.
56719  *
56720  * Fork - LGPL
56721  * <script type="text/javascript">
56722  */
56723   
56724
56725   
56726 Roo.grid.PropertyRecord = Roo.data.Record.create([
56727     {name:'name',type:'string'},  'value'
56728 ]);
56729
56730
56731 Roo.grid.PropertyStore = function(grid, source){
56732     this.grid = grid;
56733     this.store = new Roo.data.Store({
56734         recordType : Roo.grid.PropertyRecord
56735     });
56736     this.store.on('update', this.onUpdate,  this);
56737     if(source){
56738         this.setSource(source);
56739     }
56740     Roo.grid.PropertyStore.superclass.constructor.call(this);
56741 };
56742
56743
56744
56745 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56746     setSource : function(o){
56747         this.source = o;
56748         this.store.removeAll();
56749         var data = [];
56750         for(var k in o){
56751             if(this.isEditableValue(o[k])){
56752                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56753             }
56754         }
56755         this.store.loadRecords({records: data}, {}, true);
56756     },
56757
56758     onUpdate : function(ds, record, type){
56759         if(type == Roo.data.Record.EDIT){
56760             var v = record.data['value'];
56761             var oldValue = record.modified['value'];
56762             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56763                 this.source[record.id] = v;
56764                 record.commit();
56765                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56766             }else{
56767                 record.reject();
56768             }
56769         }
56770     },
56771
56772     getProperty : function(row){
56773        return this.store.getAt(row);
56774     },
56775
56776     isEditableValue: function(val){
56777         if(val && val instanceof Date){
56778             return true;
56779         }else if(typeof val == 'object' || typeof val == 'function'){
56780             return false;
56781         }
56782         return true;
56783     },
56784
56785     setValue : function(prop, value){
56786         this.source[prop] = value;
56787         this.store.getById(prop).set('value', value);
56788     },
56789
56790     getSource : function(){
56791         return this.source;
56792     }
56793 });
56794
56795 Roo.grid.PropertyColumnModel = function(grid, store){
56796     this.grid = grid;
56797     var g = Roo.grid;
56798     g.PropertyColumnModel.superclass.constructor.call(this, [
56799         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56800         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56801     ]);
56802     this.store = store;
56803     this.bselect = Roo.DomHelper.append(document.body, {
56804         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56805             {tag: 'option', value: 'true', html: 'true'},
56806             {tag: 'option', value: 'false', html: 'false'}
56807         ]
56808     });
56809     Roo.id(this.bselect);
56810     var f = Roo.form;
56811     this.editors = {
56812         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56813         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56814         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56815         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56816         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56817     };
56818     this.renderCellDelegate = this.renderCell.createDelegate(this);
56819     this.renderPropDelegate = this.renderProp.createDelegate(this);
56820 };
56821
56822 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56823     
56824     
56825     nameText : 'Name',
56826     valueText : 'Value',
56827     
56828     dateFormat : 'm/j/Y',
56829     
56830     
56831     renderDate : function(dateVal){
56832         return dateVal.dateFormat(this.dateFormat);
56833     },
56834
56835     renderBool : function(bVal){
56836         return bVal ? 'true' : 'false';
56837     },
56838
56839     isCellEditable : function(colIndex, rowIndex){
56840         return colIndex == 1;
56841     },
56842
56843     getRenderer : function(col){
56844         return col == 1 ?
56845             this.renderCellDelegate : this.renderPropDelegate;
56846     },
56847
56848     renderProp : function(v){
56849         return this.getPropertyName(v);
56850     },
56851
56852     renderCell : function(val){
56853         var rv = val;
56854         if(val instanceof Date){
56855             rv = this.renderDate(val);
56856         }else if(typeof val == 'boolean'){
56857             rv = this.renderBool(val);
56858         }
56859         return Roo.util.Format.htmlEncode(rv);
56860     },
56861
56862     getPropertyName : function(name){
56863         var pn = this.grid.propertyNames;
56864         return pn && pn[name] ? pn[name] : name;
56865     },
56866
56867     getCellEditor : function(colIndex, rowIndex){
56868         var p = this.store.getProperty(rowIndex);
56869         var n = p.data['name'], val = p.data['value'];
56870         
56871         if(typeof(this.grid.customEditors[n]) == 'string'){
56872             return this.editors[this.grid.customEditors[n]];
56873         }
56874         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56875             return this.grid.customEditors[n];
56876         }
56877         if(val instanceof Date){
56878             return this.editors['date'];
56879         }else if(typeof val == 'number'){
56880             return this.editors['number'];
56881         }else if(typeof val == 'boolean'){
56882             return this.editors['boolean'];
56883         }else{
56884             return this.editors['string'];
56885         }
56886     }
56887 });
56888
56889 /**
56890  * @class Roo.grid.PropertyGrid
56891  * @extends Roo.grid.EditorGrid
56892  * This class represents the  interface of a component based property grid control.
56893  * <br><br>Usage:<pre><code>
56894  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56895       
56896  });
56897  // set any options
56898  grid.render();
56899  * </code></pre>
56900   
56901  * @constructor
56902  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56903  * The container MUST have some type of size defined for the grid to fill. The container will be
56904  * automatically set to position relative if it isn't already.
56905  * @param {Object} config A config object that sets properties on this grid.
56906  */
56907 Roo.grid.PropertyGrid = function(container, config){
56908     config = config || {};
56909     var store = new Roo.grid.PropertyStore(this);
56910     this.store = store;
56911     var cm = new Roo.grid.PropertyColumnModel(this, store);
56912     store.store.sort('name', 'ASC');
56913     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56914         ds: store.store,
56915         cm: cm,
56916         enableColLock:false,
56917         enableColumnMove:false,
56918         stripeRows:false,
56919         trackMouseOver: false,
56920         clicksToEdit:1
56921     }, config));
56922     this.getGridEl().addClass('x-props-grid');
56923     this.lastEditRow = null;
56924     this.on('columnresize', this.onColumnResize, this);
56925     this.addEvents({
56926          /**
56927              * @event beforepropertychange
56928              * Fires before a property changes (return false to stop?)
56929              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56930              * @param {String} id Record Id
56931              * @param {String} newval New Value
56932          * @param {String} oldval Old Value
56933              */
56934         "beforepropertychange": true,
56935         /**
56936              * @event propertychange
56937              * Fires after a property changes
56938              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56939              * @param {String} id Record Id
56940              * @param {String} newval New Value
56941          * @param {String} oldval Old Value
56942              */
56943         "propertychange": true
56944     });
56945     this.customEditors = this.customEditors || {};
56946 };
56947 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56948     
56949      /**
56950      * @cfg {Object} customEditors map of colnames=> custom editors.
56951      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56952      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56953      * false disables editing of the field.
56954          */
56955     
56956       /**
56957      * @cfg {Object} propertyNames map of property Names to their displayed value
56958          */
56959     
56960     render : function(){
56961         Roo.grid.PropertyGrid.superclass.render.call(this);
56962         this.autoSize.defer(100, this);
56963     },
56964
56965     autoSize : function(){
56966         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56967         if(this.view){
56968             this.view.fitColumns();
56969         }
56970     },
56971
56972     onColumnResize : function(){
56973         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56974         this.autoSize();
56975     },
56976     /**
56977      * Sets the data for the Grid
56978      * accepts a Key => Value object of all the elements avaiable.
56979      * @param {Object} data  to appear in grid.
56980      */
56981     setSource : function(source){
56982         this.store.setSource(source);
56983         //this.autoSize();
56984     },
56985     /**
56986      * Gets all the data from the grid.
56987      * @return {Object} data  data stored in grid
56988      */
56989     getSource : function(){
56990         return this.store.getSource();
56991     }
56992 });/*
56993   
56994  * Licence LGPL
56995  
56996  */
56997  
56998 /**
56999  * @class Roo.grid.Calendar
57000  * @extends Roo.util.Grid
57001  * This class extends the Grid to provide a calendar widget
57002  * <br><br>Usage:<pre><code>
57003  var grid = new Roo.grid.Calendar("my-container-id", {
57004      ds: myDataStore,
57005      cm: myColModel,
57006      selModel: mySelectionModel,
57007      autoSizeColumns: true,
57008      monitorWindowResize: false,
57009      trackMouseOver: true
57010      eventstore : real data store..
57011  });
57012  // set any options
57013  grid.render();
57014   
57015   * @constructor
57016  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57017  * The container MUST have some type of size defined for the grid to fill. The container will be
57018  * automatically set to position relative if it isn't already.
57019  * @param {Object} config A config object that sets properties on this grid.
57020  */
57021 Roo.grid.Calendar = function(container, config){
57022         // initialize the container
57023         this.container = Roo.get(container);
57024         this.container.update("");
57025         this.container.setStyle("overflow", "hidden");
57026     this.container.addClass('x-grid-container');
57027
57028     this.id = this.container.id;
57029
57030     Roo.apply(this, config);
57031     // check and correct shorthanded configs
57032     
57033     var rows = [];
57034     var d =1;
57035     for (var r = 0;r < 6;r++) {
57036         
57037         rows[r]=[];
57038         for (var c =0;c < 7;c++) {
57039             rows[r][c]= '';
57040         }
57041     }
57042     if (this.eventStore) {
57043         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57044         this.eventStore.on('load',this.onLoad, this);
57045         this.eventStore.on('beforeload',this.clearEvents, this);
57046          
57047     }
57048     
57049     this.dataSource = new Roo.data.Store({
57050             proxy: new Roo.data.MemoryProxy(rows),
57051             reader: new Roo.data.ArrayReader({}, [
57052                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57053     });
57054
57055     this.dataSource.load();
57056     this.ds = this.dataSource;
57057     this.ds.xmodule = this.xmodule || false;
57058     
57059     
57060     var cellRender = function(v,x,r)
57061     {
57062         return String.format(
57063             '<div class="fc-day  fc-widget-content"><div>' +
57064                 '<div class="fc-event-container"></div>' +
57065                 '<div class="fc-day-number">{0}</div>'+
57066                 
57067                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57068             '</div></div>', v);
57069     
57070     }
57071     
57072     
57073     this.colModel = new Roo.grid.ColumnModel( [
57074         {
57075             xtype: 'ColumnModel',
57076             xns: Roo.grid,
57077             dataIndex : 'weekday0',
57078             header : 'Sunday',
57079             renderer : cellRender
57080         },
57081         {
57082             xtype: 'ColumnModel',
57083             xns: Roo.grid,
57084             dataIndex : 'weekday1',
57085             header : 'Monday',
57086             renderer : cellRender
57087         },
57088         {
57089             xtype: 'ColumnModel',
57090             xns: Roo.grid,
57091             dataIndex : 'weekday2',
57092             header : 'Tuesday',
57093             renderer : cellRender
57094         },
57095         {
57096             xtype: 'ColumnModel',
57097             xns: Roo.grid,
57098             dataIndex : 'weekday3',
57099             header : 'Wednesday',
57100             renderer : cellRender
57101         },
57102         {
57103             xtype: 'ColumnModel',
57104             xns: Roo.grid,
57105             dataIndex : 'weekday4',
57106             header : 'Thursday',
57107             renderer : cellRender
57108         },
57109         {
57110             xtype: 'ColumnModel',
57111             xns: Roo.grid,
57112             dataIndex : 'weekday5',
57113             header : 'Friday',
57114             renderer : cellRender
57115         },
57116         {
57117             xtype: 'ColumnModel',
57118             xns: Roo.grid,
57119             dataIndex : 'weekday6',
57120             header : 'Saturday',
57121             renderer : cellRender
57122         }
57123     ]);
57124     this.cm = this.colModel;
57125     this.cm.xmodule = this.xmodule || false;
57126  
57127         
57128           
57129     //this.selModel = new Roo.grid.CellSelectionModel();
57130     //this.sm = this.selModel;
57131     //this.selModel.init(this);
57132     
57133     
57134     if(this.width){
57135         this.container.setWidth(this.width);
57136     }
57137
57138     if(this.height){
57139         this.container.setHeight(this.height);
57140     }
57141     /** @private */
57142         this.addEvents({
57143         // raw events
57144         /**
57145          * @event click
57146          * The raw click event for the entire grid.
57147          * @param {Roo.EventObject} e
57148          */
57149         "click" : true,
57150         /**
57151          * @event dblclick
57152          * The raw dblclick event for the entire grid.
57153          * @param {Roo.EventObject} e
57154          */
57155         "dblclick" : true,
57156         /**
57157          * @event contextmenu
57158          * The raw contextmenu event for the entire grid.
57159          * @param {Roo.EventObject} e
57160          */
57161         "contextmenu" : true,
57162         /**
57163          * @event mousedown
57164          * The raw mousedown event for the entire grid.
57165          * @param {Roo.EventObject} e
57166          */
57167         "mousedown" : true,
57168         /**
57169          * @event mouseup
57170          * The raw mouseup event for the entire grid.
57171          * @param {Roo.EventObject} e
57172          */
57173         "mouseup" : true,
57174         /**
57175          * @event mouseover
57176          * The raw mouseover event for the entire grid.
57177          * @param {Roo.EventObject} e
57178          */
57179         "mouseover" : true,
57180         /**
57181          * @event mouseout
57182          * The raw mouseout event for the entire grid.
57183          * @param {Roo.EventObject} e
57184          */
57185         "mouseout" : true,
57186         /**
57187          * @event keypress
57188          * The raw keypress event for the entire grid.
57189          * @param {Roo.EventObject} e
57190          */
57191         "keypress" : true,
57192         /**
57193          * @event keydown
57194          * The raw keydown event for the entire grid.
57195          * @param {Roo.EventObject} e
57196          */
57197         "keydown" : true,
57198
57199         // custom events
57200
57201         /**
57202          * @event cellclick
57203          * Fires when a cell is clicked
57204          * @param {Grid} this
57205          * @param {Number} rowIndex
57206          * @param {Number} columnIndex
57207          * @param {Roo.EventObject} e
57208          */
57209         "cellclick" : true,
57210         /**
57211          * @event celldblclick
57212          * Fires when a cell is double clicked
57213          * @param {Grid} this
57214          * @param {Number} rowIndex
57215          * @param {Number} columnIndex
57216          * @param {Roo.EventObject} e
57217          */
57218         "celldblclick" : true,
57219         /**
57220          * @event rowclick
57221          * Fires when a row is clicked
57222          * @param {Grid} this
57223          * @param {Number} rowIndex
57224          * @param {Roo.EventObject} e
57225          */
57226         "rowclick" : true,
57227         /**
57228          * @event rowdblclick
57229          * Fires when a row is double clicked
57230          * @param {Grid} this
57231          * @param {Number} rowIndex
57232          * @param {Roo.EventObject} e
57233          */
57234         "rowdblclick" : true,
57235         /**
57236          * @event headerclick
57237          * Fires when a header is clicked
57238          * @param {Grid} this
57239          * @param {Number} columnIndex
57240          * @param {Roo.EventObject} e
57241          */
57242         "headerclick" : true,
57243         /**
57244          * @event headerdblclick
57245          * Fires when a header cell is double clicked
57246          * @param {Grid} this
57247          * @param {Number} columnIndex
57248          * @param {Roo.EventObject} e
57249          */
57250         "headerdblclick" : true,
57251         /**
57252          * @event rowcontextmenu
57253          * Fires when a row is right clicked
57254          * @param {Grid} this
57255          * @param {Number} rowIndex
57256          * @param {Roo.EventObject} e
57257          */
57258         "rowcontextmenu" : true,
57259         /**
57260          * @event cellcontextmenu
57261          * Fires when a cell is right clicked
57262          * @param {Grid} this
57263          * @param {Number} rowIndex
57264          * @param {Number} cellIndex
57265          * @param {Roo.EventObject} e
57266          */
57267          "cellcontextmenu" : true,
57268         /**
57269          * @event headercontextmenu
57270          * Fires when a header is right clicked
57271          * @param {Grid} this
57272          * @param {Number} columnIndex
57273          * @param {Roo.EventObject} e
57274          */
57275         "headercontextmenu" : true,
57276         /**
57277          * @event bodyscroll
57278          * Fires when the body element is scrolled
57279          * @param {Number} scrollLeft
57280          * @param {Number} scrollTop
57281          */
57282         "bodyscroll" : true,
57283         /**
57284          * @event columnresize
57285          * Fires when the user resizes a column
57286          * @param {Number} columnIndex
57287          * @param {Number} newSize
57288          */
57289         "columnresize" : true,
57290         /**
57291          * @event columnmove
57292          * Fires when the user moves a column
57293          * @param {Number} oldIndex
57294          * @param {Number} newIndex
57295          */
57296         "columnmove" : true,
57297         /**
57298          * @event startdrag
57299          * Fires when row(s) start being dragged
57300          * @param {Grid} this
57301          * @param {Roo.GridDD} dd The drag drop object
57302          * @param {event} e The raw browser event
57303          */
57304         "startdrag" : true,
57305         /**
57306          * @event enddrag
57307          * Fires when a drag operation is complete
57308          * @param {Grid} this
57309          * @param {Roo.GridDD} dd The drag drop object
57310          * @param {event} e The raw browser event
57311          */
57312         "enddrag" : true,
57313         /**
57314          * @event dragdrop
57315          * Fires when dragged row(s) are dropped on a valid DD target
57316          * @param {Grid} this
57317          * @param {Roo.GridDD} dd The drag drop object
57318          * @param {String} targetId The target drag drop object
57319          * @param {event} e The raw browser event
57320          */
57321         "dragdrop" : true,
57322         /**
57323          * @event dragover
57324          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57325          * @param {Grid} this
57326          * @param {Roo.GridDD} dd The drag drop object
57327          * @param {String} targetId The target drag drop object
57328          * @param {event} e The raw browser event
57329          */
57330         "dragover" : true,
57331         /**
57332          * @event dragenter
57333          *  Fires when the dragged row(s) first cross another DD target while being dragged
57334          * @param {Grid} this
57335          * @param {Roo.GridDD} dd The drag drop object
57336          * @param {String} targetId The target drag drop object
57337          * @param {event} e The raw browser event
57338          */
57339         "dragenter" : true,
57340         /**
57341          * @event dragout
57342          * Fires when the dragged row(s) leave another DD target while being dragged
57343          * @param {Grid} this
57344          * @param {Roo.GridDD} dd The drag drop object
57345          * @param {String} targetId The target drag drop object
57346          * @param {event} e The raw browser event
57347          */
57348         "dragout" : true,
57349         /**
57350          * @event rowclass
57351          * Fires when a row is rendered, so you can change add a style to it.
57352          * @param {GridView} gridview   The grid view
57353          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57354          */
57355         'rowclass' : true,
57356
57357         /**
57358          * @event render
57359          * Fires when the grid is rendered
57360          * @param {Grid} grid
57361          */
57362         'render' : true,
57363             /**
57364              * @event select
57365              * Fires when a date is selected
57366              * @param {DatePicker} this
57367              * @param {Date} date The selected date
57368              */
57369         'select': true,
57370         /**
57371              * @event monthchange
57372              * Fires when the displayed month changes 
57373              * @param {DatePicker} this
57374              * @param {Date} date The selected month
57375              */
57376         'monthchange': true,
57377         /**
57378              * @event evententer
57379              * Fires when mouse over an event
57380              * @param {Calendar} this
57381              * @param {event} Event
57382              */
57383         'evententer': true,
57384         /**
57385              * @event eventleave
57386              * Fires when the mouse leaves an
57387              * @param {Calendar} this
57388              * @param {event}
57389              */
57390         'eventleave': true,
57391         /**
57392              * @event eventclick
57393              * Fires when the mouse click an
57394              * @param {Calendar} this
57395              * @param {event}
57396              */
57397         'eventclick': true,
57398         /**
57399              * @event eventrender
57400              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57401              * @param {Calendar} this
57402              * @param {data} data to be modified
57403              */
57404         'eventrender': true
57405         
57406     });
57407
57408     Roo.grid.Grid.superclass.constructor.call(this);
57409     this.on('render', function() {
57410         this.view.el.addClass('x-grid-cal'); 
57411         
57412         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57413
57414     },this);
57415     
57416     if (!Roo.grid.Calendar.style) {
57417         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57418             
57419             
57420             '.x-grid-cal .x-grid-col' :  {
57421                 height: 'auto !important',
57422                 'vertical-align': 'top'
57423             },
57424             '.x-grid-cal  .fc-event-hori' : {
57425                 height: '14px'
57426             }
57427              
57428             
57429         }, Roo.id());
57430     }
57431
57432     
57433     
57434 };
57435 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57436     /**
57437      * @cfg {Store} eventStore The store that loads events.
57438      */
57439     eventStore : 25,
57440
57441      
57442     activeDate : false,
57443     startDay : 0,
57444     autoWidth : true,
57445     monitorWindowResize : false,
57446
57447     
57448     resizeColumns : function() {
57449         var col = (this.view.el.getWidth() / 7) - 3;
57450         // loop through cols, and setWidth
57451         for(var i =0 ; i < 7 ; i++){
57452             this.cm.setColumnWidth(i, col);
57453         }
57454     },
57455      setDate :function(date) {
57456         
57457         Roo.log('setDate?');
57458         
57459         this.resizeColumns();
57460         var vd = this.activeDate;
57461         this.activeDate = date;
57462 //        if(vd && this.el){
57463 //            var t = date.getTime();
57464 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57465 //                Roo.log('using add remove');
57466 //                
57467 //                this.fireEvent('monthchange', this, date);
57468 //                
57469 //                this.cells.removeClass("fc-state-highlight");
57470 //                this.cells.each(function(c){
57471 //                   if(c.dateValue == t){
57472 //                       c.addClass("fc-state-highlight");
57473 //                       setTimeout(function(){
57474 //                            try{c.dom.firstChild.focus();}catch(e){}
57475 //                       }, 50);
57476 //                       return false;
57477 //                   }
57478 //                   return true;
57479 //                });
57480 //                return;
57481 //            }
57482 //        }
57483         
57484         var days = date.getDaysInMonth();
57485         
57486         var firstOfMonth = date.getFirstDateOfMonth();
57487         var startingPos = firstOfMonth.getDay()-this.startDay;
57488         
57489         if(startingPos < this.startDay){
57490             startingPos += 7;
57491         }
57492         
57493         var pm = date.add(Date.MONTH, -1);
57494         var prevStart = pm.getDaysInMonth()-startingPos;
57495 //        
57496         
57497         
57498         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57499         
57500         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57501         //this.cells.addClassOnOver('fc-state-hover');
57502         
57503         var cells = this.cells.elements;
57504         var textEls = this.textNodes;
57505         
57506         //Roo.each(cells, function(cell){
57507         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57508         //});
57509         
57510         days += startingPos;
57511
57512         // convert everything to numbers so it's fast
57513         var day = 86400000;
57514         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57515         //Roo.log(d);
57516         //Roo.log(pm);
57517         //Roo.log(prevStart);
57518         
57519         var today = new Date().clearTime().getTime();
57520         var sel = date.clearTime().getTime();
57521         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57522         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57523         var ddMatch = this.disabledDatesRE;
57524         var ddText = this.disabledDatesText;
57525         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57526         var ddaysText = this.disabledDaysText;
57527         var format = this.format;
57528         
57529         var setCellClass = function(cal, cell){
57530             
57531             //Roo.log('set Cell Class');
57532             cell.title = "";
57533             var t = d.getTime();
57534             
57535             //Roo.log(d);
57536             
57537             
57538             cell.dateValue = t;
57539             if(t == today){
57540                 cell.className += " fc-today";
57541                 cell.className += " fc-state-highlight";
57542                 cell.title = cal.todayText;
57543             }
57544             if(t == sel){
57545                 // disable highlight in other month..
57546                 cell.className += " fc-state-highlight";
57547                 
57548             }
57549             // disabling
57550             if(t < min) {
57551                 //cell.className = " fc-state-disabled";
57552                 cell.title = cal.minText;
57553                 return;
57554             }
57555             if(t > max) {
57556                 //cell.className = " fc-state-disabled";
57557                 cell.title = cal.maxText;
57558                 return;
57559             }
57560             if(ddays){
57561                 if(ddays.indexOf(d.getDay()) != -1){
57562                     // cell.title = ddaysText;
57563                    // cell.className = " fc-state-disabled";
57564                 }
57565             }
57566             if(ddMatch && format){
57567                 var fvalue = d.dateFormat(format);
57568                 if(ddMatch.test(fvalue)){
57569                     cell.title = ddText.replace("%0", fvalue);
57570                    cell.className = " fc-state-disabled";
57571                 }
57572             }
57573             
57574             if (!cell.initialClassName) {
57575                 cell.initialClassName = cell.dom.className;
57576             }
57577             
57578             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57579         };
57580
57581         var i = 0;
57582         
57583         for(; i < startingPos; i++) {
57584             cells[i].dayName =  (++prevStart);
57585             Roo.log(textEls[i]);
57586             d.setDate(d.getDate()+1);
57587             
57588             //cells[i].className = "fc-past fc-other-month";
57589             setCellClass(this, cells[i]);
57590         }
57591         
57592         var intDay = 0;
57593         
57594         for(; i < days; i++){
57595             intDay = i - startingPos + 1;
57596             cells[i].dayName =  (intDay);
57597             d.setDate(d.getDate()+1);
57598             
57599             cells[i].className = ''; // "x-date-active";
57600             setCellClass(this, cells[i]);
57601         }
57602         var extraDays = 0;
57603         
57604         for(; i < 42; i++) {
57605             //textEls[i].innerHTML = (++extraDays);
57606             
57607             d.setDate(d.getDate()+1);
57608             cells[i].dayName = (++extraDays);
57609             cells[i].className = "fc-future fc-other-month";
57610             setCellClass(this, cells[i]);
57611         }
57612         
57613         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57614         
57615         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57616         
57617         // this will cause all the cells to mis
57618         var rows= [];
57619         var i =0;
57620         for (var r = 0;r < 6;r++) {
57621             for (var c =0;c < 7;c++) {
57622                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57623             }    
57624         }
57625         
57626         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57627         for(i=0;i<cells.length;i++) {
57628             
57629             this.cells.elements[i].dayName = cells[i].dayName ;
57630             this.cells.elements[i].className = cells[i].className;
57631             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57632             this.cells.elements[i].title = cells[i].title ;
57633             this.cells.elements[i].dateValue = cells[i].dateValue ;
57634         }
57635         
57636         
57637         
57638         
57639         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57640         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57641         
57642         ////if(totalRows != 6){
57643             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57644            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57645        // }
57646         
57647         this.fireEvent('monthchange', this, date);
57648         
57649         
57650     },
57651  /**
57652      * Returns the grid's SelectionModel.
57653      * @return {SelectionModel}
57654      */
57655     getSelectionModel : function(){
57656         if(!this.selModel){
57657             this.selModel = new Roo.grid.CellSelectionModel();
57658         }
57659         return this.selModel;
57660     },
57661
57662     load: function() {
57663         this.eventStore.load()
57664         
57665         
57666         
57667     },
57668     
57669     findCell : function(dt) {
57670         dt = dt.clearTime().getTime();
57671         var ret = false;
57672         this.cells.each(function(c){
57673             //Roo.log("check " +c.dateValue + '?=' + dt);
57674             if(c.dateValue == dt){
57675                 ret = c;
57676                 return false;
57677             }
57678             return true;
57679         });
57680         
57681         return ret;
57682     },
57683     
57684     findCells : function(rec) {
57685         var s = rec.data.start_dt.clone().clearTime().getTime();
57686        // Roo.log(s);
57687         var e= rec.data.end_dt.clone().clearTime().getTime();
57688        // Roo.log(e);
57689         var ret = [];
57690         this.cells.each(function(c){
57691              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57692             
57693             if(c.dateValue > e){
57694                 return ;
57695             }
57696             if(c.dateValue < s){
57697                 return ;
57698             }
57699             ret.push(c);
57700         });
57701         
57702         return ret;    
57703     },
57704     
57705     findBestRow: function(cells)
57706     {
57707         var ret = 0;
57708         
57709         for (var i =0 ; i < cells.length;i++) {
57710             ret  = Math.max(cells[i].rows || 0,ret);
57711         }
57712         return ret;
57713         
57714     },
57715     
57716     
57717     addItem : function(rec)
57718     {
57719         // look for vertical location slot in
57720         var cells = this.findCells(rec);
57721         
57722         rec.row = this.findBestRow(cells);
57723         
57724         // work out the location.
57725         
57726         var crow = false;
57727         var rows = [];
57728         for(var i =0; i < cells.length; i++) {
57729             if (!crow) {
57730                 crow = {
57731                     start : cells[i],
57732                     end :  cells[i]
57733                 };
57734                 continue;
57735             }
57736             if (crow.start.getY() == cells[i].getY()) {
57737                 // on same row.
57738                 crow.end = cells[i];
57739                 continue;
57740             }
57741             // different row.
57742             rows.push(crow);
57743             crow = {
57744                 start: cells[i],
57745                 end : cells[i]
57746             };
57747             
57748         }
57749         
57750         rows.push(crow);
57751         rec.els = [];
57752         rec.rows = rows;
57753         rec.cells = cells;
57754         for (var i = 0; i < cells.length;i++) {
57755             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57756             
57757         }
57758         
57759         
57760     },
57761     
57762     clearEvents: function() {
57763         
57764         if (!this.eventStore.getCount()) {
57765             return;
57766         }
57767         // reset number of rows in cells.
57768         Roo.each(this.cells.elements, function(c){
57769             c.rows = 0;
57770         });
57771         
57772         this.eventStore.each(function(e) {
57773             this.clearEvent(e);
57774         },this);
57775         
57776     },
57777     
57778     clearEvent : function(ev)
57779     {
57780         if (ev.els) {
57781             Roo.each(ev.els, function(el) {
57782                 el.un('mouseenter' ,this.onEventEnter, this);
57783                 el.un('mouseleave' ,this.onEventLeave, this);
57784                 el.remove();
57785             },this);
57786             ev.els = [];
57787         }
57788     },
57789     
57790     
57791     renderEvent : function(ev,ctr) {
57792         if (!ctr) {
57793              ctr = this.view.el.select('.fc-event-container',true).first();
57794         }
57795         
57796          
57797         this.clearEvent(ev);
57798             //code
57799        
57800         
57801         
57802         ev.els = [];
57803         var cells = ev.cells;
57804         var rows = ev.rows;
57805         this.fireEvent('eventrender', this, ev);
57806         
57807         for(var i =0; i < rows.length; i++) {
57808             
57809             cls = '';
57810             if (i == 0) {
57811                 cls += ' fc-event-start';
57812             }
57813             if ((i+1) == rows.length) {
57814                 cls += ' fc-event-end';
57815             }
57816             
57817             //Roo.log(ev.data);
57818             // how many rows should it span..
57819             var cg = this.eventTmpl.append(ctr,Roo.apply({
57820                 fccls : cls
57821                 
57822             }, ev.data) , true);
57823             
57824             
57825             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57826             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57827             cg.on('click', this.onEventClick, this, ev);
57828             
57829             ev.els.push(cg);
57830             
57831             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57832             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57833             //Roo.log(cg);
57834              
57835             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57836             cg.setWidth(ebox.right - sbox.x -2);
57837         }
57838     },
57839     
57840     renderEvents: function()
57841     {   
57842         // first make sure there is enough space..
57843         
57844         if (!this.eventTmpl) {
57845             this.eventTmpl = new Roo.Template(
57846                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57847                     '<div class="fc-event-inner">' +
57848                         '<span class="fc-event-time">{time}</span>' +
57849                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57850                     '</div>' +
57851                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57852                 '</div>'
57853             );
57854                 
57855         }
57856                
57857         
57858         
57859         this.cells.each(function(c) {
57860             //Roo.log(c.select('.fc-day-content div',true).first());
57861             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57862         });
57863         
57864         var ctr = this.view.el.select('.fc-event-container',true).first();
57865         
57866         var cls;
57867         this.eventStore.each(function(ev){
57868             
57869             this.renderEvent(ev);
57870              
57871              
57872         }, this);
57873         this.view.layout();
57874         
57875     },
57876     
57877     onEventEnter: function (e, el,event,d) {
57878         this.fireEvent('evententer', this, el, event);
57879     },
57880     
57881     onEventLeave: function (e, el,event,d) {
57882         this.fireEvent('eventleave', this, el, event);
57883     },
57884     
57885     onEventClick: function (e, el,event,d) {
57886         this.fireEvent('eventclick', this, el, event);
57887     },
57888     
57889     onMonthChange: function () {
57890         this.store.load();
57891     },
57892     
57893     onLoad: function () {
57894         
57895         //Roo.log('calendar onload');
57896 //         
57897         if(this.eventStore.getCount() > 0){
57898             
57899            
57900             
57901             this.eventStore.each(function(d){
57902                 
57903                 
57904                 // FIXME..
57905                 var add =   d.data;
57906                 if (typeof(add.end_dt) == 'undefined')  {
57907                     Roo.log("Missing End time in calendar data: ");
57908                     Roo.log(d);
57909                     return;
57910                 }
57911                 if (typeof(add.start_dt) == 'undefined')  {
57912                     Roo.log("Missing Start time in calendar data: ");
57913                     Roo.log(d);
57914                     return;
57915                 }
57916                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57917                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57918                 add.id = add.id || d.id;
57919                 add.title = add.title || '??';
57920                 
57921                 this.addItem(d);
57922                 
57923              
57924             },this);
57925         }
57926         
57927         this.renderEvents();
57928     }
57929     
57930
57931 });
57932 /*
57933  grid : {
57934                 xtype: 'Grid',
57935                 xns: Roo.grid,
57936                 listeners : {
57937                     render : function ()
57938                     {
57939                         _this.grid = this;
57940                         
57941                         if (!this.view.el.hasClass('course-timesheet')) {
57942                             this.view.el.addClass('course-timesheet');
57943                         }
57944                         if (this.tsStyle) {
57945                             this.ds.load({});
57946                             return; 
57947                         }
57948                         Roo.log('width');
57949                         Roo.log(_this.grid.view.el.getWidth());
57950                         
57951                         
57952                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57953                             '.course-timesheet .x-grid-row' : {
57954                                 height: '80px'
57955                             },
57956                             '.x-grid-row td' : {
57957                                 'vertical-align' : 0
57958                             },
57959                             '.course-edit-link' : {
57960                                 'color' : 'blue',
57961                                 'text-overflow' : 'ellipsis',
57962                                 'overflow' : 'hidden',
57963                                 'white-space' : 'nowrap',
57964                                 'cursor' : 'pointer'
57965                             },
57966                             '.sub-link' : {
57967                                 'color' : 'green'
57968                             },
57969                             '.de-act-sup-link' : {
57970                                 'color' : 'purple',
57971                                 'text-decoration' : 'line-through'
57972                             },
57973                             '.de-act-link' : {
57974                                 'color' : 'red',
57975                                 'text-decoration' : 'line-through'
57976                             },
57977                             '.course-timesheet .course-highlight' : {
57978                                 'border-top-style': 'dashed !important',
57979                                 'border-bottom-bottom': 'dashed !important'
57980                             },
57981                             '.course-timesheet .course-item' : {
57982                                 'font-family'   : 'tahoma, arial, helvetica',
57983                                 'font-size'     : '11px',
57984                                 'overflow'      : 'hidden',
57985                                 'padding-left'  : '10px',
57986                                 'padding-right' : '10px',
57987                                 'padding-top' : '10px' 
57988                             }
57989                             
57990                         }, Roo.id());
57991                                 this.ds.load({});
57992                     }
57993                 },
57994                 autoWidth : true,
57995                 monitorWindowResize : false,
57996                 cellrenderer : function(v,x,r)
57997                 {
57998                     return v;
57999                 },
58000                 sm : {
58001                     xtype: 'CellSelectionModel',
58002                     xns: Roo.grid
58003                 },
58004                 dataSource : {
58005                     xtype: 'Store',
58006                     xns: Roo.data,
58007                     listeners : {
58008                         beforeload : function (_self, options)
58009                         {
58010                             options.params = options.params || {};
58011                             options.params._month = _this.monthField.getValue();
58012                             options.params.limit = 9999;
58013                             options.params['sort'] = 'when_dt';    
58014                             options.params['dir'] = 'ASC';    
58015                             this.proxy.loadResponse = this.loadResponse;
58016                             Roo.log("load?");
58017                             //this.addColumns();
58018                         },
58019                         load : function (_self, records, options)
58020                         {
58021                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58022                                 // if you click on the translation.. you can edit it...
58023                                 var el = Roo.get(this);
58024                                 var id = el.dom.getAttribute('data-id');
58025                                 var d = el.dom.getAttribute('data-date');
58026                                 var t = el.dom.getAttribute('data-time');
58027                                 //var id = this.child('span').dom.textContent;
58028                                 
58029                                 //Roo.log(this);
58030                                 Pman.Dialog.CourseCalendar.show({
58031                                     id : id,
58032                                     when_d : d,
58033                                     when_t : t,
58034                                     productitem_active : id ? 1 : 0
58035                                 }, function() {
58036                                     _this.grid.ds.load({});
58037                                 });
58038                            
58039                            });
58040                            
58041                            _this.panel.fireEvent('resize', [ '', '' ]);
58042                         }
58043                     },
58044                     loadResponse : function(o, success, response){
58045                             // this is overridden on before load..
58046                             
58047                             Roo.log("our code?");       
58048                             //Roo.log(success);
58049                             //Roo.log(response)
58050                             delete this.activeRequest;
58051                             if(!success){
58052                                 this.fireEvent("loadexception", this, o, response);
58053                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58054                                 return;
58055                             }
58056                             var result;
58057                             try {
58058                                 result = o.reader.read(response);
58059                             }catch(e){
58060                                 Roo.log("load exception?");
58061                                 this.fireEvent("loadexception", this, o, response, e);
58062                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58063                                 return;
58064                             }
58065                             Roo.log("ready...");        
58066                             // loop through result.records;
58067                             // and set this.tdate[date] = [] << array of records..
58068                             _this.tdata  = {};
58069                             Roo.each(result.records, function(r){
58070                                 //Roo.log(r.data);
58071                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58072                                     _this.tdata[r.data.when_dt.format('j')] = [];
58073                                 }
58074                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58075                             });
58076                             
58077                             //Roo.log(_this.tdata);
58078                             
58079                             result.records = [];
58080                             result.totalRecords = 6;
58081                     
58082                             // let's generate some duumy records for the rows.
58083                             //var st = _this.dateField.getValue();
58084                             
58085                             // work out monday..
58086                             //st = st.add(Date.DAY, -1 * st.format('w'));
58087                             
58088                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58089                             
58090                             var firstOfMonth = date.getFirstDayOfMonth();
58091                             var days = date.getDaysInMonth();
58092                             var d = 1;
58093                             var firstAdded = false;
58094                             for (var i = 0; i < result.totalRecords ; i++) {
58095                                 //var d= st.add(Date.DAY, i);
58096                                 var row = {};
58097                                 var added = 0;
58098                                 for(var w = 0 ; w < 7 ; w++){
58099                                     if(!firstAdded && firstOfMonth != w){
58100                                         continue;
58101                                     }
58102                                     if(d > days){
58103                                         continue;
58104                                     }
58105                                     firstAdded = true;
58106                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58107                                     row['weekday'+w] = String.format(
58108                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58109                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58110                                                     d,
58111                                                     date.format('Y-m-')+dd
58112                                                 );
58113                                     added++;
58114                                     if(typeof(_this.tdata[d]) != 'undefined'){
58115                                         Roo.each(_this.tdata[d], function(r){
58116                                             var is_sub = '';
58117                                             var deactive = '';
58118                                             var id = r.id;
58119                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58120                                             if(r.parent_id*1>0){
58121                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58122                                                 id = r.parent_id;
58123                                             }
58124                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58125                                                 deactive = 'de-act-link';
58126                                             }
58127                                             
58128                                             row['weekday'+w] += String.format(
58129                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58130                                                     id, //0
58131                                                     r.product_id_name, //1
58132                                                     r.when_dt.format('h:ia'), //2
58133                                                     is_sub, //3
58134                                                     deactive, //4
58135                                                     desc // 5
58136                                             );
58137                                         });
58138                                     }
58139                                     d++;
58140                                 }
58141                                 
58142                                 // only do this if something added..
58143                                 if(added > 0){ 
58144                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58145                                 }
58146                                 
58147                                 
58148                                 // push it twice. (second one with an hour..
58149                                 
58150                             }
58151                             //Roo.log(result);
58152                             this.fireEvent("load", this, o, o.request.arg);
58153                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58154                         },
58155                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58156                     proxy : {
58157                         xtype: 'HttpProxy',
58158                         xns: Roo.data,
58159                         method : 'GET',
58160                         url : baseURL + '/Roo/Shop_course.php'
58161                     },
58162                     reader : {
58163                         xtype: 'JsonReader',
58164                         xns: Roo.data,
58165                         id : 'id',
58166                         fields : [
58167                             {
58168                                 'name': 'id',
58169                                 'type': 'int'
58170                             },
58171                             {
58172                                 'name': 'when_dt',
58173                                 'type': 'string'
58174                             },
58175                             {
58176                                 'name': 'end_dt',
58177                                 'type': 'string'
58178                             },
58179                             {
58180                                 'name': 'parent_id',
58181                                 'type': 'int'
58182                             },
58183                             {
58184                                 'name': 'product_id',
58185                                 'type': 'int'
58186                             },
58187                             {
58188                                 'name': 'productitem_id',
58189                                 'type': 'int'
58190                             },
58191                             {
58192                                 'name': 'guid',
58193                                 'type': 'int'
58194                             }
58195                         ]
58196                     }
58197                 },
58198                 toolbar : {
58199                     xtype: 'Toolbar',
58200                     xns: Roo,
58201                     items : [
58202                         {
58203                             xtype: 'Button',
58204                             xns: Roo.Toolbar,
58205                             listeners : {
58206                                 click : function (_self, e)
58207                                 {
58208                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58209                                     sd.setMonth(sd.getMonth()-1);
58210                                     _this.monthField.setValue(sd.format('Y-m-d'));
58211                                     _this.grid.ds.load({});
58212                                 }
58213                             },
58214                             text : "Back"
58215                         },
58216                         {
58217                             xtype: 'Separator',
58218                             xns: Roo.Toolbar
58219                         },
58220                         {
58221                             xtype: 'MonthField',
58222                             xns: Roo.form,
58223                             listeners : {
58224                                 render : function (_self)
58225                                 {
58226                                     _this.monthField = _self;
58227                                    // _this.monthField.set  today
58228                                 },
58229                                 select : function (combo, date)
58230                                 {
58231                                     _this.grid.ds.load({});
58232                                 }
58233                             },
58234                             value : (function() { return new Date(); })()
58235                         },
58236                         {
58237                             xtype: 'Separator',
58238                             xns: Roo.Toolbar
58239                         },
58240                         {
58241                             xtype: 'TextItem',
58242                             xns: Roo.Toolbar,
58243                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58244                         },
58245                         {
58246                             xtype: 'Fill',
58247                             xns: Roo.Toolbar
58248                         },
58249                         {
58250                             xtype: 'Button',
58251                             xns: Roo.Toolbar,
58252                             listeners : {
58253                                 click : function (_self, e)
58254                                 {
58255                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58256                                     sd.setMonth(sd.getMonth()+1);
58257                                     _this.monthField.setValue(sd.format('Y-m-d'));
58258                                     _this.grid.ds.load({});
58259                                 }
58260                             },
58261                             text : "Next"
58262                         }
58263                     ]
58264                 },
58265                  
58266             }
58267         };
58268         
58269         *//*
58270  * Based on:
58271  * Ext JS Library 1.1.1
58272  * Copyright(c) 2006-2007, Ext JS, LLC.
58273  *
58274  * Originally Released Under LGPL - original licence link has changed is not relivant.
58275  *
58276  * Fork - LGPL
58277  * <script type="text/javascript">
58278  */
58279  
58280 /**
58281  * @class Roo.LoadMask
58282  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58283  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58284  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58285  * element's UpdateManager load indicator and will be destroyed after the initial load.
58286  * @constructor
58287  * Create a new LoadMask
58288  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58289  * @param {Object} config The config object
58290  */
58291 Roo.LoadMask = function(el, config){
58292     this.el = Roo.get(el);
58293     Roo.apply(this, config);
58294     if(this.store){
58295         this.store.on('beforeload', this.onBeforeLoad, this);
58296         this.store.on('load', this.onLoad, this);
58297         this.store.on('loadexception', this.onLoadException, this);
58298         this.removeMask = false;
58299     }else{
58300         var um = this.el.getUpdateManager();
58301         um.showLoadIndicator = false; // disable the default indicator
58302         um.on('beforeupdate', this.onBeforeLoad, this);
58303         um.on('update', this.onLoad, this);
58304         um.on('failure', this.onLoad, this);
58305         this.removeMask = true;
58306     }
58307 };
58308
58309 Roo.LoadMask.prototype = {
58310     /**
58311      * @cfg {Boolean} removeMask
58312      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58313      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58314      */
58315     /**
58316      * @cfg {String} msg
58317      * The text to display in a centered loading message box (defaults to 'Loading...')
58318      */
58319     msg : 'Loading...',
58320     /**
58321      * @cfg {String} msgCls
58322      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58323      */
58324     msgCls : 'x-mask-loading',
58325
58326     /**
58327      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58328      * @type Boolean
58329      */
58330     disabled: false,
58331
58332     /**
58333      * Disables the mask to prevent it from being displayed
58334      */
58335     disable : function(){
58336        this.disabled = true;
58337     },
58338
58339     /**
58340      * Enables the mask so that it can be displayed
58341      */
58342     enable : function(){
58343         this.disabled = false;
58344     },
58345     
58346     onLoadException : function()
58347     {
58348         Roo.log(arguments);
58349         
58350         if (typeof(arguments[3]) != 'undefined') {
58351             Roo.MessageBox.alert("Error loading",arguments[3]);
58352         } 
58353         /*
58354         try {
58355             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58356                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58357             }   
58358         } catch(e) {
58359             
58360         }
58361         */
58362     
58363         
58364         
58365         this.el.unmask(this.removeMask);
58366     },
58367     // private
58368     onLoad : function()
58369     {
58370         this.el.unmask(this.removeMask);
58371     },
58372
58373     // private
58374     onBeforeLoad : function(){
58375         if(!this.disabled){
58376             this.el.mask(this.msg, this.msgCls);
58377         }
58378     },
58379
58380     // private
58381     destroy : function(){
58382         if(this.store){
58383             this.store.un('beforeload', this.onBeforeLoad, this);
58384             this.store.un('load', this.onLoad, this);
58385             this.store.un('loadexception', this.onLoadException, this);
58386         }else{
58387             var um = this.el.getUpdateManager();
58388             um.un('beforeupdate', this.onBeforeLoad, this);
58389             um.un('update', this.onLoad, this);
58390             um.un('failure', this.onLoad, this);
58391         }
58392     }
58393 };/*
58394  * Based on:
58395  * Ext JS Library 1.1.1
58396  * Copyright(c) 2006-2007, Ext JS, LLC.
58397  *
58398  * Originally Released Under LGPL - original licence link has changed is not relivant.
58399  *
58400  * Fork - LGPL
58401  * <script type="text/javascript">
58402  */
58403
58404
58405 /**
58406  * @class Roo.XTemplate
58407  * @extends Roo.Template
58408  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58409 <pre><code>
58410 var t = new Roo.XTemplate(
58411         '&lt;select name="{name}"&gt;',
58412                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58413         '&lt;/select&gt;'
58414 );
58415  
58416 // then append, applying the master template values
58417  </code></pre>
58418  *
58419  * Supported features:
58420  *
58421  *  Tags:
58422
58423 <pre><code>
58424       {a_variable} - output encoded.
58425       {a_variable.format:("Y-m-d")} - call a method on the variable
58426       {a_variable:raw} - unencoded output
58427       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58428       {a_variable:this.method_on_template(...)} - call a method on the template object.
58429  
58430 </code></pre>
58431  *  The tpl tag:
58432 <pre><code>
58433         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58434         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58435         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58436         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58437   
58438         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58439         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58440 </code></pre>
58441  *      
58442  */
58443 Roo.XTemplate = function()
58444 {
58445     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58446     if (this.html) {
58447         this.compile();
58448     }
58449 };
58450
58451
58452 Roo.extend(Roo.XTemplate, Roo.Template, {
58453
58454     /**
58455      * The various sub templates
58456      */
58457     tpls : false,
58458     /**
58459      *
58460      * basic tag replacing syntax
58461      * WORD:WORD()
58462      *
58463      * // you can fake an object call by doing this
58464      *  x.t:(test,tesT) 
58465      * 
58466      */
58467     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58468
58469     /**
58470      * compile the template
58471      *
58472      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58473      *
58474      */
58475     compile: function()
58476     {
58477         var s = this.html;
58478      
58479         s = ['<tpl>', s, '</tpl>'].join('');
58480     
58481         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58482             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58483             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58484             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58485             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58486             m,
58487             id     = 0,
58488             tpls   = [];
58489     
58490         while(true == !!(m = s.match(re))){
58491             var forMatch   = m[0].match(nameRe),
58492                 ifMatch   = m[0].match(ifRe),
58493                 execMatch   = m[0].match(execRe),
58494                 namedMatch   = m[0].match(namedRe),
58495                 
58496                 exp  = null, 
58497                 fn   = null,
58498                 exec = null,
58499                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58500                 
58501             if (ifMatch) {
58502                 // if - puts fn into test..
58503                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58504                 if(exp){
58505                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58506                 }
58507             }
58508             
58509             if (execMatch) {
58510                 // exec - calls a function... returns empty if true is  returned.
58511                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58512                 if(exp){
58513                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58514                 }
58515             }
58516             
58517             
58518             if (name) {
58519                 // for = 
58520                 switch(name){
58521                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58522                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58523                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58524                 }
58525             }
58526             var uid = namedMatch ? namedMatch[1] : id;
58527             
58528             
58529             tpls.push({
58530                 id:     namedMatch ? namedMatch[1] : id,
58531                 target: name,
58532                 exec:   exec,
58533                 test:   fn,
58534                 body:   m[1] || ''
58535             });
58536             if (namedMatch) {
58537                 s = s.replace(m[0], '');
58538             } else { 
58539                 s = s.replace(m[0], '{xtpl'+ id + '}');
58540             }
58541             ++id;
58542         }
58543         this.tpls = [];
58544         for(var i = tpls.length-1; i >= 0; --i){
58545             this.compileTpl(tpls[i]);
58546             this.tpls[tpls[i].id] = tpls[i];
58547         }
58548         this.master = tpls[tpls.length-1];
58549         return this;
58550     },
58551     /**
58552      * same as applyTemplate, except it's done to one of the subTemplates
58553      * when using named templates, you can do:
58554      *
58555      * var str = pl.applySubTemplate('your-name', values);
58556      *
58557      * 
58558      * @param {Number} id of the template
58559      * @param {Object} values to apply to template
58560      * @param {Object} parent (normaly the instance of this object)
58561      */
58562     applySubTemplate : function(id, values, parent)
58563     {
58564         
58565         
58566         var t = this.tpls[id];
58567         
58568         
58569         try { 
58570             if(t.test && !t.test.call(this, values, parent)){
58571                 return '';
58572             }
58573         } catch(e) {
58574             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58575             Roo.log(e.toString());
58576             Roo.log(t.test);
58577             return ''
58578         }
58579         try { 
58580             
58581             if(t.exec && t.exec.call(this, values, parent)){
58582                 return '';
58583             }
58584         } catch(e) {
58585             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58586             Roo.log(e.toString());
58587             Roo.log(t.exec);
58588             return ''
58589         }
58590         try {
58591             var vs = t.target ? t.target.call(this, values, parent) : values;
58592             parent = t.target ? values : parent;
58593             if(t.target && vs instanceof Array){
58594                 var buf = [];
58595                 for(var i = 0, len = vs.length; i < len; i++){
58596                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58597                 }
58598                 return buf.join('');
58599             }
58600             return t.compiled.call(this, vs, parent);
58601         } catch (e) {
58602             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58603             Roo.log(e.toString());
58604             Roo.log(t.compiled);
58605             return '';
58606         }
58607     },
58608
58609     compileTpl : function(tpl)
58610     {
58611         var fm = Roo.util.Format;
58612         var useF = this.disableFormats !== true;
58613         var sep = Roo.isGecko ? "+" : ",";
58614         var undef = function(str) {
58615             Roo.log("Property not found :"  + str);
58616             return '';
58617         };
58618         
58619         var fn = function(m, name, format, args)
58620         {
58621             //Roo.log(arguments);
58622             args = args ? args.replace(/\\'/g,"'") : args;
58623             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58624             if (typeof(format) == 'undefined') {
58625                 format= 'htmlEncode';
58626             }
58627             if (format == 'raw' ) {
58628                 format = false;
58629             }
58630             
58631             if(name.substr(0, 4) == 'xtpl'){
58632                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58633             }
58634             
58635             // build an array of options to determine if value is undefined..
58636             
58637             // basically get 'xxxx.yyyy' then do
58638             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58639             //    (function () { Roo.log("Property not found"); return ''; })() :
58640             //    ......
58641             
58642             var udef_ar = [];
58643             var lookfor = '';
58644             Roo.each(name.split('.'), function(st) {
58645                 lookfor += (lookfor.length ? '.': '') + st;
58646                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58647             });
58648             
58649             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58650             
58651             
58652             if(format && useF){
58653                 
58654                 args = args ? ',' + args : "";
58655                  
58656                 if(format.substr(0, 5) != "this."){
58657                     format = "fm." + format + '(';
58658                 }else{
58659                     format = 'this.call("'+ format.substr(5) + '", ';
58660                     args = ", values";
58661                 }
58662                 
58663                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58664             }
58665              
58666             if (args.length) {
58667                 // called with xxyx.yuu:(test,test)
58668                 // change to ()
58669                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58670             }
58671             // raw.. - :raw modifier..
58672             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58673             
58674         };
58675         var body;
58676         // branched to use + in gecko and [].join() in others
58677         if(Roo.isGecko){
58678             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58679                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58680                     "';};};";
58681         }else{
58682             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58683             body.push(tpl.body.replace(/(\r\n|\n)/g,
58684                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58685             body.push("'].join('');};};");
58686             body = body.join('');
58687         }
58688         
58689         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58690        
58691         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58692         eval(body);
58693         
58694         return this;
58695     },
58696
58697     applyTemplate : function(values){
58698         return this.master.compiled.call(this, values, {});
58699         //var s = this.subs;
58700     },
58701
58702     apply : function(){
58703         return this.applyTemplate.apply(this, arguments);
58704     }
58705
58706  });
58707
58708 Roo.XTemplate.from = function(el){
58709     el = Roo.getDom(el);
58710     return new Roo.XTemplate(el.value || el.innerHTML);
58711 };