d4a2133a35357295be33594a793e65f99b6c1ecf
[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      * Checks whether or not the specified object exists in the array.
929      * @param {Object} o The object to check for
930      * @return {Number} The index of o in the array (or -1 if it is not found)
931      */
932     indexOf : function(o){
933        for (var i = 0, len = this.length; i < len; i++){
934               if(this[i] == o) return i;
935        }
936            return -1;
937     },
938
939     /**
940      * Removes the specified object from the array.  If the object is not found nothing happens.
941      * @param {Object} o The object to remove
942      */
943     remove : function(o){
944        var index = this.indexOf(o);
945        if(index != -1){
946            this.splice(index, 1);
947        }
948     },
949     /**
950      * Map (JS 1.6 compatibility)
951      * @param {Function} function  to call
952      */
953     map : function(fun )
954     {
955         var len = this.length >>> 0;
956         if (typeof fun != "function")
957             throw new TypeError();
958
959         var res = new Array(len);
960         var thisp = arguments[1];
961         for (var i = 0; i < len; i++)
962         {
963             if (i in this)
964                 res[i] = fun.call(thisp, this[i], i, this);
965         }
966
967         return res;
968     }
969     
970 });
971
972
973  /*
974  * Based on:
975  * Ext JS Library 1.1.1
976  * Copyright(c) 2006-2007, Ext JS, LLC.
977  *
978  * Originally Released Under LGPL - original licence link has changed is not relivant.
979  *
980  * Fork - LGPL
981  * <script type="text/javascript">
982  */
983
984 /**
985  * @class Date
986  *
987  * The date parsing and format syntax is a subset of
988  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
989  * supported will provide results equivalent to their PHP versions.
990  *
991  * Following is the list of all currently supported formats:
992  *<pre>
993 Sample date:
994 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
995
996 Format  Output      Description
997 ------  ----------  --------------------------------------------------------------
998   d      10         Day of the month, 2 digits with leading zeros
999   D      Wed        A textual representation of a day, three letters
1000   j      10         Day of the month without leading zeros
1001   l      Wednesday  A full textual representation of the day of the week
1002   S      th         English ordinal day of month suffix, 2 chars (use with j)
1003   w      3          Numeric representation of the day of the week
1004   z      9          The julian date, or day of the year (0-365)
1005   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1006   F      January    A full textual representation of the month
1007   m      01         Numeric representation of a month, with leading zeros
1008   M      Jan        Month name abbreviation, three letters
1009   n      1          Numeric representation of a month, without leading zeros
1010   t      31         Number of days in the given month
1011   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1012   Y      2007       A full numeric representation of a year, 4 digits
1013   y      07         A two digit representation of a year
1014   a      pm         Lowercase Ante meridiem and Post meridiem
1015   A      PM         Uppercase Ante meridiem and Post meridiem
1016   g      3          12-hour format of an hour without leading zeros
1017   G      15         24-hour format of an hour without leading zeros
1018   h      03         12-hour format of an hour with leading zeros
1019   H      15         24-hour format of an hour with leading zeros
1020   i      05         Minutes with leading zeros
1021   s      01         Seconds, with leading zeros
1022   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1023   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1024   T      CST        Timezone setting of the machine running the code
1025   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1026 </pre>
1027  *
1028  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1029  * <pre><code>
1030 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1031 document.write(dt.format('Y-m-d'));                         //2007-01-10
1032 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1033 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
1034  </code></pre>
1035  *
1036  * Here are some standard date/time patterns that you might find helpful.  They
1037  * are not part of the source of Date.js, but to use them you can simply copy this
1038  * block of code into any script that is included after Date.js and they will also become
1039  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1040  * <pre><code>
1041 Date.patterns = {
1042     ISO8601Long:"Y-m-d H:i:s",
1043     ISO8601Short:"Y-m-d",
1044     ShortDate: "n/j/Y",
1045     LongDate: "l, F d, Y",
1046     FullDateTime: "l, F d, Y g:i:s A",
1047     MonthDay: "F d",
1048     ShortTime: "g:i A",
1049     LongTime: "g:i:s A",
1050     SortableDateTime: "Y-m-d\\TH:i:s",
1051     UniversalSortableDateTime: "Y-m-d H:i:sO",
1052     YearMonth: "F, Y"
1053 };
1054 </code></pre>
1055  *
1056  * Example usage:
1057  * <pre><code>
1058 var dt = new Date();
1059 document.write(dt.format(Date.patterns.ShortDate));
1060  </code></pre>
1061  */
1062
1063 /*
1064  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1065  * They generate precompiled functions from date formats instead of parsing and
1066  * processing the pattern every time you format a date.  These functions are available
1067  * on every Date object (any javascript function).
1068  *
1069  * The original article and download are here:
1070  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1071  *
1072  */
1073  
1074  
1075  // was in core
1076 /**
1077  Returns the number of milliseconds between this date and date
1078  @param {Date} date (optional) Defaults to now
1079  @return {Number} The diff in milliseconds
1080  @member Date getElapsed
1081  */
1082 Date.prototype.getElapsed = function(date) {
1083         return Math.abs((date || new Date()).getTime()-this.getTime());
1084 };
1085 // was in date file..
1086
1087
1088 // private
1089 Date.parseFunctions = {count:0};
1090 // private
1091 Date.parseRegexes = [];
1092 // private
1093 Date.formatFunctions = {count:0};
1094
1095 // private
1096 Date.prototype.dateFormat = function(format) {
1097     if (Date.formatFunctions[format] == null) {
1098         Date.createNewFormat(format);
1099     }
1100     var func = Date.formatFunctions[format];
1101     return this[func]();
1102 };
1103
1104
1105 /**
1106  * Formats a date given the supplied format string
1107  * @param {String} format The format string
1108  * @return {String} The formatted date
1109  * @method
1110  */
1111 Date.prototype.format = Date.prototype.dateFormat;
1112
1113 // private
1114 Date.createNewFormat = function(format) {
1115     var funcName = "format" + Date.formatFunctions.count++;
1116     Date.formatFunctions[format] = funcName;
1117     var code = "Date.prototype." + funcName + " = function(){return ";
1118     var special = false;
1119     var ch = '';
1120     for (var i = 0; i < format.length; ++i) {
1121         ch = format.charAt(i);
1122         if (!special && ch == "\\") {
1123             special = true;
1124         }
1125         else if (special) {
1126             special = false;
1127             code += "'" + String.escape(ch) + "' + ";
1128         }
1129         else {
1130             code += Date.getFormatCode(ch);
1131         }
1132     }
1133     /** eval:var:zzzzzzzzzzzzz */
1134     eval(code.substring(0, code.length - 3) + ";}");
1135 };
1136
1137 // private
1138 Date.getFormatCode = function(character) {
1139     switch (character) {
1140     case "d":
1141         return "String.leftPad(this.getDate(), 2, '0') + ";
1142     case "D":
1143         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1144     case "j":
1145         return "this.getDate() + ";
1146     case "l":
1147         return "Date.dayNames[this.getDay()] + ";
1148     case "S":
1149         return "this.getSuffix() + ";
1150     case "w":
1151         return "this.getDay() + ";
1152     case "z":
1153         return "this.getDayOfYear() + ";
1154     case "W":
1155         return "this.getWeekOfYear() + ";
1156     case "F":
1157         return "Date.monthNames[this.getMonth()] + ";
1158     case "m":
1159         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1160     case "M":
1161         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1162     case "n":
1163         return "(this.getMonth() + 1) + ";
1164     case "t":
1165         return "this.getDaysInMonth() + ";
1166     case "L":
1167         return "(this.isLeapYear() ? 1 : 0) + ";
1168     case "Y":
1169         return "this.getFullYear() + ";
1170     case "y":
1171         return "('' + this.getFullYear()).substring(2, 4) + ";
1172     case "a":
1173         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1174     case "A":
1175         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1176     case "g":
1177         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1178     case "G":
1179         return "this.getHours() + ";
1180     case "h":
1181         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1182     case "H":
1183         return "String.leftPad(this.getHours(), 2, '0') + ";
1184     case "i":
1185         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1186     case "s":
1187         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1188     case "O":
1189         return "this.getGMTOffset() + ";
1190     case "P":
1191         return "this.getGMTColonOffset() + ";
1192     case "T":
1193         return "this.getTimezone() + ";
1194     case "Z":
1195         return "(this.getTimezoneOffset() * -60) + ";
1196     default:
1197         return "'" + String.escape(character) + "' + ";
1198     }
1199 };
1200
1201 /**
1202  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1203  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1204  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1205  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1206  * string or the parse operation will fail.
1207  * Example Usage:
1208 <pre><code>
1209 //dt = Fri May 25 2007 (current date)
1210 var dt = new Date();
1211
1212 //dt = Thu May 25 2006 (today's month/day in 2006)
1213 dt = Date.parseDate("2006", "Y");
1214
1215 //dt = Sun Jan 15 2006 (all date parts specified)
1216 dt = Date.parseDate("2006-1-15", "Y-m-d");
1217
1218 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1219 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1220 </code></pre>
1221  * @param {String} input The unparsed date as a string
1222  * @param {String} format The format the date is in
1223  * @return {Date} The parsed date
1224  * @static
1225  */
1226 Date.parseDate = function(input, format) {
1227     if (Date.parseFunctions[format] == null) {
1228         Date.createParser(format);
1229     }
1230     var func = Date.parseFunctions[format];
1231     return Date[func](input);
1232 };
1233 /**
1234  * @private
1235  */
1236 Date.createParser = function(format) {
1237     var funcName = "parse" + Date.parseFunctions.count++;
1238     var regexNum = Date.parseRegexes.length;
1239     var currentGroup = 1;
1240     Date.parseFunctions[format] = funcName;
1241
1242     var code = "Date." + funcName + " = function(input){\n"
1243         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1244         + "var d = new Date();\n"
1245         + "y = d.getFullYear();\n"
1246         + "m = d.getMonth();\n"
1247         + "d = d.getDate();\n"
1248         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1249         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1250         + "if (results && results.length > 0) {";
1251     var regex = "";
1252
1253     var special = false;
1254     var ch = '';
1255     for (var i = 0; i < format.length; ++i) {
1256         ch = format.charAt(i);
1257         if (!special && ch == "\\") {
1258             special = true;
1259         }
1260         else if (special) {
1261             special = false;
1262             regex += String.escape(ch);
1263         }
1264         else {
1265             var obj = Date.formatCodeToRegex(ch, currentGroup);
1266             currentGroup += obj.g;
1267             regex += obj.s;
1268             if (obj.g && obj.c) {
1269                 code += obj.c;
1270             }
1271         }
1272     }
1273
1274     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1275         + "{v = new Date(y, m, d, h, i, s);}\n"
1276         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1277         + "{v = new Date(y, m, d, h, i);}\n"
1278         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1279         + "{v = new Date(y, m, d, h);}\n"
1280         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1281         + "{v = new Date(y, m, d);}\n"
1282         + "else if (y >= 0 && m >= 0)\n"
1283         + "{v = new Date(y, m);}\n"
1284         + "else if (y >= 0)\n"
1285         + "{v = new Date(y);}\n"
1286         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1287         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1288         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1289         + ";}";
1290
1291     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1292     /** eval:var:zzzzzzzzzzzzz */
1293     eval(code);
1294 };
1295
1296 // private
1297 Date.formatCodeToRegex = function(character, currentGroup) {
1298     switch (character) {
1299     case "D":
1300         return {g:0,
1301         c:null,
1302         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1303     case "j":
1304         return {g:1,
1305             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1306             s:"(\\d{1,2})"}; // day of month without leading zeroes
1307     case "d":
1308         return {g:1,
1309             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1310             s:"(\\d{2})"}; // day of month with leading zeroes
1311     case "l":
1312         return {g:0,
1313             c:null,
1314             s:"(?:" + Date.dayNames.join("|") + ")"};
1315     case "S":
1316         return {g:0,
1317             c:null,
1318             s:"(?:st|nd|rd|th)"};
1319     case "w":
1320         return {g:0,
1321             c:null,
1322             s:"\\d"};
1323     case "z":
1324         return {g:0,
1325             c:null,
1326             s:"(?:\\d{1,3})"};
1327     case "W":
1328         return {g:0,
1329             c:null,
1330             s:"(?:\\d{2})"};
1331     case "F":
1332         return {g:1,
1333             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1334             s:"(" + Date.monthNames.join("|") + ")"};
1335     case "M":
1336         return {g:1,
1337             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1338             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1339     case "n":
1340         return {g:1,
1341             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1342             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1343     case "m":
1344         return {g:1,
1345             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1346             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1347     case "t":
1348         return {g:0,
1349             c:null,
1350             s:"\\d{1,2}"};
1351     case "L":
1352         return {g:0,
1353             c:null,
1354             s:"(?:1|0)"};
1355     case "Y":
1356         return {g:1,
1357             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{4})"};
1359     case "y":
1360         return {g:1,
1361             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1362                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1363             s:"(\\d{1,2})"};
1364     case "a":
1365         return {g:1,
1366             c:"if (results[" + currentGroup + "] == 'am') {\n"
1367                 + "if (h == 12) { h = 0; }\n"
1368                 + "} else { if (h < 12) { h += 12; }}",
1369             s:"(am|pm)"};
1370     case "A":
1371         return {g:1,
1372             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1373                 + "if (h == 12) { h = 0; }\n"
1374                 + "} else { if (h < 12) { h += 12; }}",
1375             s:"(AM|PM)"};
1376     case "g":
1377     case "G":
1378         return {g:1,
1379             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1381     case "h":
1382     case "H":
1383         return {g:1,
1384             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1386     case "i":
1387         return {g:1,
1388             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1389             s:"(\\d{2})"};
1390     case "s":
1391         return {g:1,
1392             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"};
1394     case "O":
1395         return {g:1,
1396             c:[
1397                 "o = results[", currentGroup, "];\n",
1398                 "var sn = o.substring(0,1);\n", // get + / - sign
1399                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1400                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1401                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1402                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1403             ].join(""),
1404             s:"([+\-]\\d{2,4})"};
1405     
1406     
1407     case "P":
1408         return {g:1,
1409                 c:[
1410                    "o = results[", currentGroup, "];\n",
1411                    "var sn = o.substring(0,1);\n",
1412                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1413                    "var mn = o.substring(4,6) % 60;\n",
1414                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1415                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1416             ].join(""),
1417             s:"([+\-]\\d{4})"};
1418     case "T":
1419         return {g:0,
1420             c:null,
1421             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1422     case "Z":
1423         return {g:1,
1424             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1425                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1426             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1427     default:
1428         return {g:0,
1429             c:null,
1430             s:String.escape(character)};
1431     }
1432 };
1433
1434 /**
1435  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1436  * @return {String} The abbreviated timezone name (e.g. 'CST')
1437  */
1438 Date.prototype.getTimezone = function() {
1439     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1440 };
1441
1442 /**
1443  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1444  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1445  */
1446 Date.prototype.getGMTOffset = function() {
1447     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1448         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1449         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1450 };
1451
1452 /**
1453  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1454  * @return {String} 2-characters representing hours and 2-characters representing minutes
1455  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1456  */
1457 Date.prototype.getGMTColonOffset = function() {
1458         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460                 + ":"
1461                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1462 }
1463
1464 /**
1465  * Get the numeric day number of the year, adjusted for leap year.
1466  * @return {Number} 0 through 364 (365 in leap years)
1467  */
1468 Date.prototype.getDayOfYear = function() {
1469     var num = 0;
1470     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1471     for (var i = 0; i < this.getMonth(); ++i) {
1472         num += Date.daysInMonth[i];
1473     }
1474     return num + this.getDate() - 1;
1475 };
1476
1477 /**
1478  * Get the string representation of the numeric week number of the year
1479  * (equivalent to the format specifier 'W').
1480  * @return {String} '00' through '52'
1481  */
1482 Date.prototype.getWeekOfYear = function() {
1483     // Skip to Thursday of this week
1484     var now = this.getDayOfYear() + (4 - this.getDay());
1485     // Find the first Thursday of the year
1486     var jan1 = new Date(this.getFullYear(), 0, 1);
1487     var then = (7 - jan1.getDay() + 4);
1488     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1489 };
1490
1491 /**
1492  * Whether or not the current date is in a leap year.
1493  * @return {Boolean} True if the current date is in a leap year, else false
1494  */
1495 Date.prototype.isLeapYear = function() {
1496     var year = this.getFullYear();
1497     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1498 };
1499
1500 /**
1501  * Get the first day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getFirstDayOfMonth = function() {
1512     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516 /**
1517  * Get the last day of the current month, adjusted for leap year.  The returned value
1518  * is the numeric day index within the week (0-6) which can be used in conjunction with
1519  * the {@link #monthNames} array to retrieve the textual day name.
1520  * Example:
1521  *<pre><code>
1522 var dt = new Date('1/10/2007');
1523 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1524 </code></pre>
1525  * @return {Number} The day number (0-6)
1526  */
1527 Date.prototype.getLastDayOfMonth = function() {
1528     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1529     return (day < 0) ? (day + 7) : day;
1530 };
1531
1532
1533 /**
1534  * Get the first date of this date's month
1535  * @return {Date}
1536  */
1537 Date.prototype.getFirstDateOfMonth = function() {
1538     return new Date(this.getFullYear(), this.getMonth(), 1);
1539 };
1540
1541 /**
1542  * Get the last date of this date's month
1543  * @return {Date}
1544  */
1545 Date.prototype.getLastDateOfMonth = function() {
1546     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1547 };
1548 /**
1549  * Get the number of days in the current month, adjusted for leap year.
1550  * @return {Number} The number of days in the month
1551  */
1552 Date.prototype.getDaysInMonth = function() {
1553     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1554     return Date.daysInMonth[this.getMonth()];
1555 };
1556
1557 /**
1558  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1559  * @return {String} 'st, 'nd', 'rd' or 'th'
1560  */
1561 Date.prototype.getSuffix = function() {
1562     switch (this.getDate()) {
1563         case 1:
1564         case 21:
1565         case 31:
1566             return "st";
1567         case 2:
1568         case 22:
1569             return "nd";
1570         case 3:
1571         case 23:
1572             return "rd";
1573         default:
1574             return "th";
1575     }
1576 };
1577
1578 // private
1579 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1580
1581 /**
1582  * An array of textual month names.
1583  * Override these values for international dates, for example...
1584  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1585  * @type Array
1586  * @static
1587  */
1588 Date.monthNames =
1589    ["January",
1590     "February",
1591     "March",
1592     "April",
1593     "May",
1594     "June",
1595     "July",
1596     "August",
1597     "September",
1598     "October",
1599     "November",
1600     "December"];
1601
1602 /**
1603  * An array of textual day names.
1604  * Override these values for international dates, for example...
1605  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1606  * @type Array
1607  * @static
1608  */
1609 Date.dayNames =
1610    ["Sunday",
1611     "Monday",
1612     "Tuesday",
1613     "Wednesday",
1614     "Thursday",
1615     "Friday",
1616     "Saturday"];
1617
1618 // private
1619 Date.y2kYear = 50;
1620 // private
1621 Date.monthNumbers = {
1622     Jan:0,
1623     Feb:1,
1624     Mar:2,
1625     Apr:3,
1626     May:4,
1627     Jun:5,
1628     Jul:6,
1629     Aug:7,
1630     Sep:8,
1631     Oct:9,
1632     Nov:10,
1633     Dec:11};
1634
1635 /**
1636  * Creates and returns a new Date instance with the exact same date value as the called instance.
1637  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1638  * variable will also be changed.  When the intention is to create a new variable that will not
1639  * modify the original instance, you should create a clone.
1640  *
1641  * Example of correctly cloning a date:
1642  * <pre><code>
1643 //wrong way:
1644 var orig = new Date('10/1/2006');
1645 var copy = orig;
1646 copy.setDate(5);
1647 document.write(orig);  //returns 'Thu Oct 05 2006'!
1648
1649 //correct way:
1650 var orig = new Date('10/1/2006');
1651 var copy = orig.clone();
1652 copy.setDate(5);
1653 document.write(orig);  //returns 'Thu Oct 01 2006'
1654 </code></pre>
1655  * @return {Date} The new Date instance
1656  */
1657 Date.prototype.clone = function() {
1658         return new Date(this.getTime());
1659 };
1660
1661 /**
1662  * Clears any time information from this date
1663  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1664  @return {Date} this or the clone
1665  */
1666 Date.prototype.clearTime = function(clone){
1667     if(clone){
1668         return this.clone().clearTime();
1669     }
1670     this.setHours(0);
1671     this.setMinutes(0);
1672     this.setSeconds(0);
1673     this.setMilliseconds(0);
1674     return this;
1675 };
1676
1677 // private
1678 // safari setMonth is broken
1679 if(Roo.isSafari){
1680     Date.brokenSetMonth = Date.prototype.setMonth;
1681         Date.prototype.setMonth = function(num){
1682                 if(num <= -1){
1683                         var n = Math.ceil(-num);
1684                         var back_year = Math.ceil(n/12);
1685                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1686                         this.setFullYear(this.getFullYear() - back_year);
1687                         return Date.brokenSetMonth.call(this, month);
1688                 } else {
1689                         return Date.brokenSetMonth.apply(this, arguments);
1690                 }
1691         };
1692 }
1693
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.MILLI = "ms";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.SECOND = "s";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MINUTE = "mi";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.HOUR = "h";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.DAY = "d";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MONTH = "mo";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.YEAR = "y";
1722
1723 /**
1724  * Provides a convenient method of performing basic date arithmetic.  This method
1725  * does not modify the Date instance being called - it creates and returns
1726  * a new Date instance containing the resulting date value.
1727  *
1728  * Examples:
1729  * <pre><code>
1730 //Basic usage:
1731 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1732 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1733
1734 //Negative values will subtract correctly:
1735 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1736 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1737
1738 //You can even chain several calls together in one line!
1739 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1740 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1741  </code></pre>
1742  *
1743  * @param {String} interval   A valid date interval enum value
1744  * @param {Number} value      The amount to add to the current date
1745  * @return {Date} The new Date instance
1746  */
1747 Date.prototype.add = function(interval, value){
1748   var d = this.clone();
1749   if (!interval || value === 0) return d;
1750   switch(interval.toLowerCase()){
1751     case Date.MILLI:
1752       d.setMilliseconds(this.getMilliseconds() + value);
1753       break;
1754     case Date.SECOND:
1755       d.setSeconds(this.getSeconds() + value);
1756       break;
1757     case Date.MINUTE:
1758       d.setMinutes(this.getMinutes() + value);
1759       break;
1760     case Date.HOUR:
1761       d.setHours(this.getHours() + value);
1762       break;
1763     case Date.DAY:
1764       d.setDate(this.getDate() + value);
1765       break;
1766     case Date.MONTH:
1767       var day = this.getDate();
1768       if(day > 28){
1769           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1770       }
1771       d.setDate(day);
1772       d.setMonth(this.getMonth() + value);
1773       break;
1774     case Date.YEAR:
1775       d.setFullYear(this.getFullYear() + value);
1776       break;
1777   }
1778   return d;
1779 };
1780 /*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.lib.Dom
1793  * @static
1794  * 
1795  * Dom utils (from YIU afaik)
1796  * 
1797  **/
1798 Roo.lib.Dom = {
1799     /**
1800      * Get the view width
1801      * @param {Boolean} full True will get the full document, otherwise it's the view width
1802      * @return {Number} The width
1803      */
1804      
1805     getViewWidth : function(full) {
1806         return full ? this.getDocumentWidth() : this.getViewportWidth();
1807     },
1808     /**
1809      * Get the view height
1810      * @param {Boolean} full True will get the full document, otherwise it's the view height
1811      * @return {Number} The height
1812      */
1813     getViewHeight : function(full) {
1814         return full ? this.getDocumentHeight() : this.getViewportHeight();
1815     },
1816
1817     getDocumentHeight: function() {
1818         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1819         return Math.max(scrollHeight, this.getViewportHeight());
1820     },
1821
1822     getDocumentWidth: function() {
1823         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1824         return Math.max(scrollWidth, this.getViewportWidth());
1825     },
1826
1827     getViewportHeight: function() {
1828         var height = self.innerHeight;
1829         var mode = document.compatMode;
1830
1831         if ((mode || Roo.isIE) && !Roo.isOpera) {
1832             height = (mode == "CSS1Compat") ?
1833                      document.documentElement.clientHeight :
1834                      document.body.clientHeight;
1835         }
1836
1837         return height;
1838     },
1839
1840     getViewportWidth: function() {
1841         var width = self.innerWidth;
1842         var mode = document.compatMode;
1843
1844         if (mode || Roo.isIE) {
1845             width = (mode == "CSS1Compat") ?
1846                     document.documentElement.clientWidth :
1847                     document.body.clientWidth;
1848         }
1849         return width;
1850     },
1851
1852     isAncestor : function(p, c) {
1853         p = Roo.getDom(p);
1854         c = Roo.getDom(c);
1855         if (!p || !c) {
1856             return false;
1857         }
1858
1859         if (p.contains && !Roo.isSafari) {
1860             return p.contains(c);
1861         } else if (p.compareDocumentPosition) {
1862             return !!(p.compareDocumentPosition(c) & 16);
1863         } else {
1864             var parent = c.parentNode;
1865             while (parent) {
1866                 if (parent == p) {
1867                     return true;
1868                 }
1869                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1870                     return false;
1871                 }
1872                 parent = parent.parentNode;
1873             }
1874             return false;
1875         }
1876     },
1877
1878     getRegion : function(el) {
1879         return Roo.lib.Region.getRegion(el);
1880     },
1881
1882     getY : function(el) {
1883         return this.getXY(el)[1];
1884     },
1885
1886     getX : function(el) {
1887         return this.getXY(el)[0];
1888     },
1889
1890     getXY : function(el) {
1891         var p, pe, b, scroll, bd = document.body;
1892         el = Roo.getDom(el);
1893         var fly = Roo.lib.AnimBase.fly;
1894         if (el.getBoundingClientRect) {
1895             b = el.getBoundingClientRect();
1896             scroll = fly(document).getScroll();
1897             return [b.left + scroll.left, b.top + scroll.top];
1898         }
1899         var x = 0, y = 0;
1900
1901         p = el;
1902
1903         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1904
1905         while (p) {
1906
1907             x += p.offsetLeft;
1908             y += p.offsetTop;
1909
1910             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1911                 hasAbsolute = true;
1912             }
1913
1914             if (Roo.isGecko) {
1915                 pe = fly(p);
1916
1917                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1918                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1919
1920
1921                 x += bl;
1922                 y += bt;
1923
1924
1925                 if (p != el && pe.getStyle('overflow') != 'visible') {
1926                     x += bl;
1927                     y += bt;
1928                 }
1929             }
1930             p = p.offsetParent;
1931         }
1932
1933         if (Roo.isSafari && hasAbsolute) {
1934             x -= bd.offsetLeft;
1935             y -= bd.offsetTop;
1936         }
1937
1938         if (Roo.isGecko && !hasAbsolute) {
1939             var dbd = fly(bd);
1940             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1941             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1942         }
1943
1944         p = el.parentNode;
1945         while (p && p != bd) {
1946             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1947                 x -= p.scrollLeft;
1948                 y -= p.scrollTop;
1949             }
1950             p = p.parentNode;
1951         }
1952         return [x, y];
1953     },
1954  
1955   
1956
1957
1958     setXY : function(el, xy) {
1959         el = Roo.fly(el, '_setXY');
1960         el.position();
1961         var pts = el.translatePoints(xy);
1962         if (xy[0] !== false) {
1963             el.dom.style.left = pts.left + "px";
1964         }
1965         if (xy[1] !== false) {
1966             el.dom.style.top = pts.top + "px";
1967         }
1968     },
1969
1970     setX : function(el, x) {
1971         this.setXY(el, [x, false]);
1972     },
1973
1974     setY : function(el, y) {
1975         this.setXY(el, [false, y]);
1976     }
1977 };
1978 /*
1979  * Portions of this file are based on pieces of Yahoo User Interface Library
1980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1981  * YUI licensed under the BSD License:
1982  * http://developer.yahoo.net/yui/license.txt
1983  * <script type="text/javascript">
1984  *
1985  */
1986
1987 Roo.lib.Event = function() {
1988     var loadComplete = false;
1989     var listeners = [];
1990     var unloadListeners = [];
1991     var retryCount = 0;
1992     var onAvailStack = [];
1993     var counter = 0;
1994     var lastError = null;
1995
1996     return {
1997         POLL_RETRYS: 200,
1998         POLL_INTERVAL: 20,
1999         EL: 0,
2000         TYPE: 1,
2001         FN: 2,
2002         WFN: 3,
2003         OBJ: 3,
2004         ADJ_SCOPE: 4,
2005         _interval: null,
2006
2007         startInterval: function() {
2008             if (!this._interval) {
2009                 var self = this;
2010                 var callback = function() {
2011                     self._tryPreloadAttach();
2012                 };
2013                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2014
2015             }
2016         },
2017
2018         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2019             onAvailStack.push({ id:         p_id,
2020                 fn:         p_fn,
2021                 obj:        p_obj,
2022                 override:   p_override,
2023                 checkReady: false    });
2024
2025             retryCount = this.POLL_RETRYS;
2026             this.startInterval();
2027         },
2028
2029
2030         addListener: function(el, eventName, fn) {
2031             el = Roo.getDom(el);
2032             if (!el || !fn) {
2033                 return false;
2034             }
2035
2036             if ("unload" == eventName) {
2037                 unloadListeners[unloadListeners.length] =
2038                 [el, eventName, fn];
2039                 return true;
2040             }
2041
2042             var wrappedFn = function(e) {
2043                 return fn(Roo.lib.Event.getEvent(e));
2044             };
2045
2046             var li = [el, eventName, fn, wrappedFn];
2047
2048             var index = listeners.length;
2049             listeners[index] = li;
2050
2051             this.doAdd(el, eventName, wrappedFn, false);
2052             return true;
2053
2054         },
2055
2056
2057         removeListener: function(el, eventName, fn) {
2058             var i, len;
2059
2060             el = Roo.getDom(el);
2061
2062             if(!fn) {
2063                 return this.purgeElement(el, false, eventName);
2064             }
2065
2066
2067             if ("unload" == eventName) {
2068
2069                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2070                     var li = unloadListeners[i];
2071                     if (li &&
2072                         li[0] == el &&
2073                         li[1] == eventName &&
2074                         li[2] == fn) {
2075                         unloadListeners.splice(i, 1);
2076                         return true;
2077                     }
2078                 }
2079
2080                 return false;
2081             }
2082
2083             var cacheItem = null;
2084
2085
2086             var index = arguments[3];
2087
2088             if ("undefined" == typeof index) {
2089                 index = this._getCacheIndex(el, eventName, fn);
2090             }
2091
2092             if (index >= 0) {
2093                 cacheItem = listeners[index];
2094             }
2095
2096             if (!el || !cacheItem) {
2097                 return false;
2098             }
2099
2100             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2101
2102             delete listeners[index][this.WFN];
2103             delete listeners[index][this.FN];
2104             listeners.splice(index, 1);
2105
2106             return true;
2107
2108         },
2109
2110
2111         getTarget: function(ev, resolveTextNode) {
2112             ev = ev.browserEvent || ev;
2113             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2114             var t = ev.target || ev.srcElement;
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         resolveTextNode: function(node) {
2120             if (Roo.isSafari && node && 3 == node.nodeType) {
2121                 return node.parentNode;
2122             } else {
2123                 return node;
2124             }
2125         },
2126
2127
2128         getPageX: function(ev) {
2129             ev = ev.browserEvent || ev;
2130             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2131             var x = ev.pageX;
2132             if (!x && 0 !== x) {
2133                 x = ev.clientX || 0;
2134
2135                 if (Roo.isIE) {
2136                     x += this.getScroll()[1];
2137                 }
2138             }
2139
2140             return x;
2141         },
2142
2143
2144         getPageY: function(ev) {
2145             ev = ev.browserEvent || ev;
2146             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2147             var y = ev.pageY;
2148             if (!y && 0 !== y) {
2149                 y = ev.clientY || 0;
2150
2151                 if (Roo.isIE) {
2152                     y += this.getScroll()[0];
2153                 }
2154             }
2155
2156
2157             return y;
2158         },
2159
2160
2161         getXY: function(ev) {
2162             ev = ev.browserEvent || ev;
2163             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2164             return [this.getPageX(ev), this.getPageY(ev)];
2165         },
2166
2167
2168         getRelatedTarget: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2171             var t = ev.relatedTarget;
2172             if (!t) {
2173                 if (ev.type == "mouseout") {
2174                     t = ev.toElement;
2175                 } else if (ev.type == "mouseover") {
2176                     t = ev.fromElement;
2177                 }
2178             }
2179
2180             return this.resolveTextNode(t);
2181         },
2182
2183
2184         getTime: function(ev) {
2185             ev = ev.browserEvent || ev;
2186             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2187             if (!ev.time) {
2188                 var t = new Date().getTime();
2189                 try {
2190                     ev.time = t;
2191                 } catch(ex) {
2192                     this.lastError = ex;
2193                     return t;
2194                 }
2195             }
2196
2197             return ev.time;
2198         },
2199
2200
2201         stopEvent: function(ev) {
2202             this.stopPropagation(ev);
2203             this.preventDefault(ev);
2204         },
2205
2206
2207         stopPropagation: function(ev) {
2208             ev = ev.browserEvent || ev;
2209             if (ev.stopPropagation) {
2210                 ev.stopPropagation();
2211             } else {
2212                 ev.cancelBubble = true;
2213             }
2214         },
2215
2216
2217         preventDefault: function(ev) {
2218             ev = ev.browserEvent || ev;
2219             if(ev.preventDefault) {
2220                 ev.preventDefault();
2221             } else {
2222                 ev.returnValue = false;
2223             }
2224         },
2225
2226
2227         getEvent: function(e) {
2228             var ev = e || window.event;
2229             if (!ev) {
2230                 var c = this.getEvent.caller;
2231                 while (c) {
2232                     ev = c.arguments[0];
2233                     if (ev && Event == ev.constructor) {
2234                         break;
2235                     }
2236                     c = c.caller;
2237                 }
2238             }
2239             return ev;
2240         },
2241
2242
2243         getCharCode: function(ev) {
2244             ev = ev.browserEvent || ev;
2245             return ev.charCode || ev.keyCode || 0;
2246         },
2247
2248
2249         _getCacheIndex: function(el, eventName, fn) {
2250             for (var i = 0,len = listeners.length; i < len; ++i) {
2251                 var li = listeners[i];
2252                 if (li &&
2253                     li[this.FN] == fn &&
2254                     li[this.EL] == el &&
2255                     li[this.TYPE] == eventName) {
2256                     return i;
2257                 }
2258             }
2259
2260             return -1;
2261         },
2262
2263
2264         elCache: {},
2265
2266
2267         getEl: function(id) {
2268             return document.getElementById(id);
2269         },
2270
2271
2272         clearCache: function() {
2273         },
2274
2275
2276         _load: function(e) {
2277             loadComplete = true;
2278             var EU = Roo.lib.Event;
2279
2280
2281             if (Roo.isIE) {
2282                 EU.doRemove(window, "load", EU._load);
2283             }
2284         },
2285
2286
2287         _tryPreloadAttach: function() {
2288
2289             if (this.locked) {
2290                 return false;
2291             }
2292
2293             this.locked = true;
2294
2295
2296             var tryAgain = !loadComplete;
2297             if (!tryAgain) {
2298                 tryAgain = (retryCount > 0);
2299             }
2300
2301
2302             var notAvail = [];
2303             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2304                 var item = onAvailStack[i];
2305                 if (item) {
2306                     var el = this.getEl(item.id);
2307
2308                     if (el) {
2309                         if (!item.checkReady ||
2310                             loadComplete ||
2311                             el.nextSibling ||
2312                             (document && document.body)) {
2313
2314                             var scope = el;
2315                             if (item.override) {
2316                                 if (item.override === true) {
2317                                     scope = item.obj;
2318                                 } else {
2319                                     scope = item.override;
2320                                 }
2321                             }
2322                             item.fn.call(scope, item.obj);
2323                             onAvailStack[i] = null;
2324                         }
2325                     } else {
2326                         notAvail.push(item);
2327                     }
2328                 }
2329             }
2330
2331             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2332
2333             if (tryAgain) {
2334
2335                 this.startInterval();
2336             } else {
2337                 clearInterval(this._interval);
2338                 this._interval = null;
2339             }
2340
2341             this.locked = false;
2342
2343             return true;
2344
2345         },
2346
2347
2348         purgeElement: function(el, recurse, eventName) {
2349             var elListeners = this.getListeners(el, eventName);
2350             if (elListeners) {
2351                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2352                     var l = elListeners[i];
2353                     this.removeListener(el, l.type, l.fn);
2354                 }
2355             }
2356
2357             if (recurse && el && el.childNodes) {
2358                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2359                     this.purgeElement(el.childNodes[i], recurse, eventName);
2360                 }
2361             }
2362         },
2363
2364
2365         getListeners: function(el, eventName) {
2366             var results = [], searchLists;
2367             if (!eventName) {
2368                 searchLists = [listeners, unloadListeners];
2369             } else if (eventName == "unload") {
2370                 searchLists = [unloadListeners];
2371             } else {
2372                 searchLists = [listeners];
2373             }
2374
2375             for (var j = 0; j < searchLists.length; ++j) {
2376                 var searchList = searchLists[j];
2377                 if (searchList && searchList.length > 0) {
2378                     for (var i = 0,len = searchList.length; i < len; ++i) {
2379                         var l = searchList[i];
2380                         if (l && l[this.EL] === el &&
2381                             (!eventName || eventName === l[this.TYPE])) {
2382                             results.push({
2383                                 type:   l[this.TYPE],
2384                                 fn:     l[this.FN],
2385                                 obj:    l[this.OBJ],
2386                                 adjust: l[this.ADJ_SCOPE],
2387                                 index:  i
2388                             });
2389                         }
2390                     }
2391                 }
2392             }
2393
2394             return (results.length) ? results : null;
2395         },
2396
2397
2398         _unload: function(e) {
2399
2400             var EU = Roo.lib.Event, i, j, l, len, index;
2401
2402             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2403                 l = unloadListeners[i];
2404                 if (l) {
2405                     var scope = window;
2406                     if (l[EU.ADJ_SCOPE]) {
2407                         if (l[EU.ADJ_SCOPE] === true) {
2408                             scope = l[EU.OBJ];
2409                         } else {
2410                             scope = l[EU.ADJ_SCOPE];
2411                         }
2412                     }
2413                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2414                     unloadListeners[i] = null;
2415                     l = null;
2416                     scope = null;
2417                 }
2418             }
2419
2420             unloadListeners = null;
2421
2422             if (listeners && listeners.length > 0) {
2423                 j = listeners.length;
2424                 while (j) {
2425                     index = j - 1;
2426                     l = listeners[index];
2427                     if (l) {
2428                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2429                                 l[EU.FN], index);
2430                     }
2431                     j = j - 1;
2432                 }
2433                 l = null;
2434
2435                 EU.clearCache();
2436             }
2437
2438             EU.doRemove(window, "unload", EU._unload);
2439
2440         },
2441
2442
2443         getScroll: function() {
2444             var dd = document.documentElement, db = document.body;
2445             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2446                 return [dd.scrollTop, dd.scrollLeft];
2447             } else if (db) {
2448                 return [db.scrollTop, db.scrollLeft];
2449             } else {
2450                 return [0, 0];
2451             }
2452         },
2453
2454
2455         doAdd: function () {
2456             if (window.addEventListener) {
2457                 return function(el, eventName, fn, capture) {
2458                     el.addEventListener(eventName, fn, (capture));
2459                 };
2460             } else if (window.attachEvent) {
2461                 return function(el, eventName, fn, capture) {
2462                     el.attachEvent("on" + eventName, fn);
2463                 };
2464             } else {
2465                 return function() {
2466                 };
2467             }
2468         }(),
2469
2470
2471         doRemove: function() {
2472             if (window.removeEventListener) {
2473                 return function (el, eventName, fn, capture) {
2474                     el.removeEventListener(eventName, fn, (capture));
2475                 };
2476             } else if (window.detachEvent) {
2477                 return function (el, eventName, fn) {
2478                     el.detachEvent("on" + eventName, fn);
2479                 };
2480             } else {
2481                 return function() {
2482                 };
2483             }
2484         }()
2485     };
2486     
2487 }();
2488 (function() {     
2489    
2490     var E = Roo.lib.Event;
2491     E.on = E.addListener;
2492     E.un = E.removeListener;
2493
2494     if (document && document.body) {
2495         E._load();
2496     } else {
2497         E.doAdd(window, "load", E._load);
2498     }
2499     E.doAdd(window, "unload", E._unload);
2500     E._tryPreloadAttach();
2501 })();
2502
2503 /*
2504  * Portions of this file are based on pieces of Yahoo User Interface Library
2505  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2506  * YUI licensed under the BSD License:
2507  * http://developer.yahoo.net/yui/license.txt
2508  * <script type="text/javascript">
2509  *
2510  */
2511
2512 (function() {
2513     /**
2514      * @class Roo.lib.Ajax
2515      *
2516      */
2517     Roo.lib.Ajax = {
2518         /**
2519          * @static 
2520          */
2521         request : function(method, uri, cb, data, options) {
2522             if(options){
2523                 var hs = options.headers;
2524                 if(hs){
2525                     for(var h in hs){
2526                         if(hs.hasOwnProperty(h)){
2527                             this.initHeader(h, hs[h], false);
2528                         }
2529                     }
2530                 }
2531                 if(options.xmlData){
2532                     this.initHeader('Content-Type', 'text/xml', false);
2533                     method = 'POST';
2534                     data = options.xmlData;
2535                 }
2536             }
2537
2538             return this.asyncRequest(method, uri, cb, data);
2539         },
2540
2541         serializeForm : function(form) {
2542             if(typeof form == 'string') {
2543                 form = (document.getElementById(form) || document.forms[form]);
2544             }
2545
2546             var el, name, val, disabled, data = '', hasSubmit = false;
2547             for (var i = 0; i < form.elements.length; i++) {
2548                 el = form.elements[i];
2549                 disabled = form.elements[i].disabled;
2550                 name = form.elements[i].name;
2551                 val = form.elements[i].value;
2552
2553                 if (!disabled && name){
2554                     switch (el.type)
2555                             {
2556                         case 'select-one':
2557                         case 'select-multiple':
2558                             for (var j = 0; j < el.options.length; j++) {
2559                                 if (el.options[j].selected) {
2560                                     if (Roo.isIE) {
2561                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2562                                     }
2563                                     else {
2564                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2565                                     }
2566                                 }
2567                             }
2568                             break;
2569                         case 'radio':
2570                         case 'checkbox':
2571                             if (el.checked) {
2572                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2573                             }
2574                             break;
2575                         case 'file':
2576
2577                         case undefined:
2578
2579                         case 'reset':
2580
2581                         case 'button':
2582
2583                             break;
2584                         case 'submit':
2585                             if(hasSubmit == false) {
2586                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2587                                 hasSubmit = true;
2588                             }
2589                             break;
2590                         default:
2591                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2592                             break;
2593                     }
2594                 }
2595             }
2596             data = data.substr(0, data.length - 1);
2597             return data;
2598         },
2599
2600         headers:{},
2601
2602         hasHeaders:false,
2603
2604         useDefaultHeader:true,
2605
2606         defaultPostHeader:'application/x-www-form-urlencoded',
2607
2608         useDefaultXhrHeader:true,
2609
2610         defaultXhrHeader:'XMLHttpRequest',
2611
2612         hasDefaultHeaders:true,
2613
2614         defaultHeaders:{},
2615
2616         poll:{},
2617
2618         timeout:{},
2619
2620         pollInterval:50,
2621
2622         transactionId:0,
2623
2624         setProgId:function(id)
2625         {
2626             this.activeX.unshift(id);
2627         },
2628
2629         setDefaultPostHeader:function(b)
2630         {
2631             this.useDefaultHeader = b;
2632         },
2633
2634         setDefaultXhrHeader:function(b)
2635         {
2636             this.useDefaultXhrHeader = b;
2637         },
2638
2639         setPollingInterval:function(i)
2640         {
2641             if (typeof i == 'number' && isFinite(i)) {
2642                 this.pollInterval = i;
2643             }
2644         },
2645
2646         createXhrObject:function(transactionId)
2647         {
2648             var obj,http;
2649             try
2650             {
2651
2652                 http = new XMLHttpRequest();
2653
2654                 obj = { conn:http, tId:transactionId };
2655             }
2656             catch(e)
2657             {
2658                 for (var i = 0; i < this.activeX.length; ++i) {
2659                     try
2660                     {
2661
2662                         http = new ActiveXObject(this.activeX[i]);
2663
2664                         obj = { conn:http, tId:transactionId };
2665                         break;
2666                     }
2667                     catch(e) {
2668                     }
2669                 }
2670             }
2671             finally
2672             {
2673                 return obj;
2674             }
2675         },
2676
2677         getConnectionObject:function()
2678         {
2679             var o;
2680             var tId = this.transactionId;
2681
2682             try
2683             {
2684                 o = this.createXhrObject(tId);
2685                 if (o) {
2686                     this.transactionId++;
2687                 }
2688             }
2689             catch(e) {
2690             }
2691             finally
2692             {
2693                 return o;
2694             }
2695         },
2696
2697         asyncRequest:function(method, uri, callback, postData)
2698         {
2699             var o = this.getConnectionObject();
2700
2701             if (!o) {
2702                 return null;
2703             }
2704             else {
2705                 o.conn.open(method, uri, true);
2706
2707                 if (this.useDefaultXhrHeader) {
2708                     if (!this.defaultHeaders['X-Requested-With']) {
2709                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2710                     }
2711                 }
2712
2713                 if(postData && this.useDefaultHeader){
2714                     this.initHeader('Content-Type', this.defaultPostHeader);
2715                 }
2716
2717                  if (this.hasDefaultHeaders || this.hasHeaders) {
2718                     this.setHeader(o);
2719                 }
2720
2721                 this.handleReadyState(o, callback);
2722                 o.conn.send(postData || null);
2723
2724                 return o;
2725             }
2726         },
2727
2728         handleReadyState:function(o, callback)
2729         {
2730             var oConn = this;
2731
2732             if (callback && callback.timeout) {
2733                 
2734                 this.timeout[o.tId] = window.setTimeout(function() {
2735                     oConn.abort(o, callback, true);
2736                 }, callback.timeout);
2737             }
2738
2739             this.poll[o.tId] = window.setInterval(
2740                     function() {
2741                         if (o.conn && o.conn.readyState == 4) {
2742                             window.clearInterval(oConn.poll[o.tId]);
2743                             delete oConn.poll[o.tId];
2744
2745                             if(callback && callback.timeout) {
2746                                 window.clearTimeout(oConn.timeout[o.tId]);
2747                                 delete oConn.timeout[o.tId];
2748                             }
2749
2750                             oConn.handleTransactionResponse(o, callback);
2751                         }
2752                     }
2753                     , this.pollInterval);
2754         },
2755
2756         handleTransactionResponse:function(o, callback, isAbort)
2757         {
2758
2759             if (!callback) {
2760                 this.releaseObject(o);
2761                 return;
2762             }
2763
2764             var httpStatus, responseObject;
2765
2766             try
2767             {
2768                 if (o.conn.status !== undefined && o.conn.status != 0) {
2769                     httpStatus = o.conn.status;
2770                 }
2771                 else {
2772                     httpStatus = 13030;
2773                 }
2774             }
2775             catch(e) {
2776
2777
2778                 httpStatus = 13030;
2779             }
2780
2781             if (httpStatus >= 200 && httpStatus < 300) {
2782                 responseObject = this.createResponseObject(o, callback.argument);
2783                 if (callback.success) {
2784                     if (!callback.scope) {
2785                         callback.success(responseObject);
2786                     }
2787                     else {
2788
2789
2790                         callback.success.apply(callback.scope, [responseObject]);
2791                     }
2792                 }
2793             }
2794             else {
2795                 switch (httpStatus) {
2796
2797                     case 12002:
2798                     case 12029:
2799                     case 12030:
2800                     case 12031:
2801                     case 12152:
2802                     case 13030:
2803                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2804                         if (callback.failure) {
2805                             if (!callback.scope) {
2806                                 callback.failure(responseObject);
2807                             }
2808                             else {
2809                                 callback.failure.apply(callback.scope, [responseObject]);
2810                             }
2811                         }
2812                         break;
2813                     default:
2814                         responseObject = this.createResponseObject(o, callback.argument);
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                 }
2824             }
2825
2826             this.releaseObject(o);
2827             responseObject = null;
2828         },
2829
2830         createResponseObject:function(o, callbackArg)
2831         {
2832             var obj = {};
2833             var headerObj = {};
2834
2835             try
2836             {
2837                 var headerStr = o.conn.getAllResponseHeaders();
2838                 var header = headerStr.split('\n');
2839                 for (var i = 0; i < header.length; i++) {
2840                     var delimitPos = header[i].indexOf(':');
2841                     if (delimitPos != -1) {
2842                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2843                     }
2844                 }
2845             }
2846             catch(e) {
2847             }
2848
2849             obj.tId = o.tId;
2850             obj.status = o.conn.status;
2851             obj.statusText = o.conn.statusText;
2852             obj.getResponseHeader = headerObj;
2853             obj.getAllResponseHeaders = headerStr;
2854             obj.responseText = o.conn.responseText;
2855             obj.responseXML = o.conn.responseXML;
2856
2857             if (typeof callbackArg !== undefined) {
2858                 obj.argument = callbackArg;
2859             }
2860
2861             return obj;
2862         },
2863
2864         createExceptionObject:function(tId, callbackArg, isAbort)
2865         {
2866             var COMM_CODE = 0;
2867             var COMM_ERROR = 'communication failure';
2868             var ABORT_CODE = -1;
2869             var ABORT_ERROR = 'transaction aborted';
2870
2871             var obj = {};
2872
2873             obj.tId = tId;
2874             if (isAbort) {
2875                 obj.status = ABORT_CODE;
2876                 obj.statusText = ABORT_ERROR;
2877             }
2878             else {
2879                 obj.status = COMM_CODE;
2880                 obj.statusText = COMM_ERROR;
2881             }
2882
2883             if (callbackArg) {
2884                 obj.argument = callbackArg;
2885             }
2886
2887             return obj;
2888         },
2889
2890         initHeader:function(label, value, isDefault)
2891         {
2892             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2893
2894             if (headerObj[label] === undefined) {
2895                 headerObj[label] = value;
2896             }
2897             else {
2898
2899
2900                 headerObj[label] = value + "," + headerObj[label];
2901             }
2902
2903             if (isDefault) {
2904                 this.hasDefaultHeaders = true;
2905             }
2906             else {
2907                 this.hasHeaders = true;
2908             }
2909         },
2910
2911
2912         setHeader:function(o)
2913         {
2914             if (this.hasDefaultHeaders) {
2915                 for (var prop in this.defaultHeaders) {
2916                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2917                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2918                     }
2919                 }
2920             }
2921
2922             if (this.hasHeaders) {
2923                 for (var prop in this.headers) {
2924                     if (this.headers.hasOwnProperty(prop)) {
2925                         o.conn.setRequestHeader(prop, this.headers[prop]);
2926                     }
2927                 }
2928                 this.headers = {};
2929                 this.hasHeaders = false;
2930             }
2931         },
2932
2933         resetDefaultHeaders:function() {
2934             delete this.defaultHeaders;
2935             this.defaultHeaders = {};
2936             this.hasDefaultHeaders = false;
2937         },
2938
2939         abort:function(o, callback, isTimeout)
2940         {
2941             if(this.isCallInProgress(o)) {
2942                 o.conn.abort();
2943                 window.clearInterval(this.poll[o.tId]);
2944                 delete this.poll[o.tId];
2945                 if (isTimeout) {
2946                     delete this.timeout[o.tId];
2947                 }
2948
2949                 this.handleTransactionResponse(o, callback, true);
2950
2951                 return true;
2952             }
2953             else {
2954                 return false;
2955             }
2956         },
2957
2958
2959         isCallInProgress:function(o)
2960         {
2961             if (o && o.conn) {
2962                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2963             }
2964             else {
2965
2966                 return false;
2967             }
2968         },
2969
2970
2971         releaseObject:function(o)
2972         {
2973
2974             o.conn = null;
2975
2976             o = null;
2977         },
2978
2979         activeX:[
2980         'MSXML2.XMLHTTP.3.0',
2981         'MSXML2.XMLHTTP',
2982         'Microsoft.XMLHTTP'
2983         ]
2984
2985
2986     };
2987 })();/*
2988  * Portions of this file are based on pieces of Yahoo User Interface Library
2989  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2990  * YUI licensed under the BSD License:
2991  * http://developer.yahoo.net/yui/license.txt
2992  * <script type="text/javascript">
2993  *
2994  */
2995
2996 Roo.lib.Region = function(t, r, b, l) {
2997     this.top = t;
2998     this[1] = t;
2999     this.right = r;
3000     this.bottom = b;
3001     this.left = l;
3002     this[0] = l;
3003 };
3004
3005
3006 Roo.lib.Region.prototype = {
3007     contains : function(region) {
3008         return ( region.left >= this.left &&
3009                  region.right <= this.right &&
3010                  region.top >= this.top &&
3011                  region.bottom <= this.bottom    );
3012
3013     },
3014
3015     getArea : function() {
3016         return ( (this.bottom - this.top) * (this.right - this.left) );
3017     },
3018
3019     intersect : function(region) {
3020         var t = Math.max(this.top, region.top);
3021         var r = Math.min(this.right, region.right);
3022         var b = Math.min(this.bottom, region.bottom);
3023         var l = Math.max(this.left, region.left);
3024
3025         if (b >= t && r >= l) {
3026             return new Roo.lib.Region(t, r, b, l);
3027         } else {
3028             return null;
3029         }
3030     },
3031     union : function(region) {
3032         var t = Math.min(this.top, region.top);
3033         var r = Math.max(this.right, region.right);
3034         var b = Math.max(this.bottom, region.bottom);
3035         var l = Math.min(this.left, region.left);
3036
3037         return new Roo.lib.Region(t, r, b, l);
3038     },
3039
3040     adjust : function(t, l, b, r) {
3041         this.top += t;
3042         this.left += l;
3043         this.right += r;
3044         this.bottom += b;
3045         return this;
3046     }
3047 };
3048
3049 Roo.lib.Region.getRegion = function(el) {
3050     var p = Roo.lib.Dom.getXY(el);
3051
3052     var t = p[1];
3053     var r = p[0] + el.offsetWidth;
3054     var b = p[1] + el.offsetHeight;
3055     var l = p[0];
3056
3057     return new Roo.lib.Region(t, r, b, l);
3058 };
3059 /*
3060  * Portions of this file are based on pieces of Yahoo User Interface Library
3061  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3062  * YUI licensed under the BSD License:
3063  * http://developer.yahoo.net/yui/license.txt
3064  * <script type="text/javascript">
3065  *
3066  */
3067 //@@dep Roo.lib.Region
3068
3069
3070 Roo.lib.Point = function(x, y) {
3071     if (x instanceof Array) {
3072         y = x[1];
3073         x = x[0];
3074     }
3075     this.x = this.right = this.left = this[0] = x;
3076     this.y = this.top = this.bottom = this[1] = y;
3077 };
3078
3079 Roo.lib.Point.prototype = new Roo.lib.Region();
3080 /*
3081  * Portions of this file are based on pieces of Yahoo User Interface Library
3082  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3083  * YUI licensed under the BSD License:
3084  * http://developer.yahoo.net/yui/license.txt
3085  * <script type="text/javascript">
3086  *
3087  */
3088  
3089 (function() {   
3090
3091     Roo.lib.Anim = {
3092         scroll : function(el, args, duration, easing, cb, scope) {
3093             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3094         },
3095
3096         motion : function(el, args, duration, easing, cb, scope) {
3097             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3098         },
3099
3100         color : function(el, args, duration, easing, cb, scope) {
3101             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3102         },
3103
3104         run : function(el, args, duration, easing, cb, scope, type) {
3105             type = type || Roo.lib.AnimBase;
3106             if (typeof easing == "string") {
3107                 easing = Roo.lib.Easing[easing];
3108             }
3109             var anim = new type(el, args, duration, easing);
3110             anim.animateX(function() {
3111                 Roo.callback(cb, scope);
3112             });
3113             return anim;
3114         }
3115     };
3116 })();/*
3117  * Portions of this file are based on pieces of Yahoo User Interface Library
3118  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3119  * YUI licensed under the BSD License:
3120  * http://developer.yahoo.net/yui/license.txt
3121  * <script type="text/javascript">
3122  *
3123  */
3124
3125 (function() {    
3126     var libFlyweight;
3127     
3128     function fly(el) {
3129         if (!libFlyweight) {
3130             libFlyweight = new Roo.Element.Flyweight();
3131         }
3132         libFlyweight.dom = el;
3133         return libFlyweight;
3134     }
3135
3136     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3137     
3138    
3139     
3140     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3141         if (el) {
3142             this.init(el, attributes, duration, method);
3143         }
3144     };
3145
3146     Roo.lib.AnimBase.fly = fly;
3147     
3148     
3149     
3150     Roo.lib.AnimBase.prototype = {
3151
3152         toString: function() {
3153             var el = this.getEl();
3154             var id = el.id || el.tagName;
3155             return ("Anim " + id);
3156         },
3157
3158         patterns: {
3159             noNegatives:        /width|height|opacity|padding/i,
3160             offsetAttribute:  /^((width|height)|(top|left))$/,
3161             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3162             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3163         },
3164
3165
3166         doMethod: function(attr, start, end) {
3167             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3168         },
3169
3170
3171         setAttribute: function(attr, val, unit) {
3172             if (this.patterns.noNegatives.test(attr)) {
3173                 val = (val > 0) ? val : 0;
3174             }
3175
3176             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3177         },
3178
3179
3180         getAttribute: function(attr) {
3181             var el = this.getEl();
3182             var val = fly(el).getStyle(attr);
3183
3184             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3185                 return parseFloat(val);
3186             }
3187
3188             var a = this.patterns.offsetAttribute.exec(attr) || [];
3189             var pos = !!( a[3] );
3190             var box = !!( a[2] );
3191
3192
3193             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3194                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3195             } else {
3196                 val = 0;
3197             }
3198
3199             return val;
3200         },
3201
3202
3203         getDefaultUnit: function(attr) {
3204             if (this.patterns.defaultUnit.test(attr)) {
3205                 return 'px';
3206             }
3207
3208             return '';
3209         },
3210
3211         animateX : function(callback, scope) {
3212             var f = function() {
3213                 this.onComplete.removeListener(f);
3214                 if (typeof callback == "function") {
3215                     callback.call(scope || this, this);
3216                 }
3217             };
3218             this.onComplete.addListener(f, this);
3219             this.animate();
3220         },
3221
3222
3223         setRuntimeAttribute: function(attr) {
3224             var start;
3225             var end;
3226             var attributes = this.attributes;
3227
3228             this.runtimeAttributes[attr] = {};
3229
3230             var isset = function(prop) {
3231                 return (typeof prop !== 'undefined');
3232             };
3233
3234             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3235                 return false;
3236             }
3237
3238             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3239
3240
3241             if (isset(attributes[attr]['to'])) {
3242                 end = attributes[attr]['to'];
3243             } else if (isset(attributes[attr]['by'])) {
3244                 if (start.constructor == Array) {
3245                     end = [];
3246                     for (var i = 0, len = start.length; i < len; ++i) {
3247                         end[i] = start[i] + attributes[attr]['by'][i];
3248                     }
3249                 } else {
3250                     end = start + attributes[attr]['by'];
3251                 }
3252             }
3253
3254             this.runtimeAttributes[attr].start = start;
3255             this.runtimeAttributes[attr].end = end;
3256
3257
3258             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3259         },
3260
3261
3262         init: function(el, attributes, duration, method) {
3263
3264             var isAnimated = false;
3265
3266
3267             var startTime = null;
3268
3269
3270             var actualFrames = 0;
3271
3272
3273             el = Roo.getDom(el);
3274
3275
3276             this.attributes = attributes || {};
3277
3278
3279             this.duration = duration || 1;
3280
3281
3282             this.method = method || Roo.lib.Easing.easeNone;
3283
3284
3285             this.useSeconds = true;
3286
3287
3288             this.currentFrame = 0;
3289
3290
3291             this.totalFrames = Roo.lib.AnimMgr.fps;
3292
3293
3294             this.getEl = function() {
3295                 return el;
3296             };
3297
3298
3299             this.isAnimated = function() {
3300                 return isAnimated;
3301             };
3302
3303
3304             this.getStartTime = function() {
3305                 return startTime;
3306             };
3307
3308             this.runtimeAttributes = {};
3309
3310
3311             this.animate = function() {
3312                 if (this.isAnimated()) {
3313                     return false;
3314                 }
3315
3316                 this.currentFrame = 0;
3317
3318                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3319
3320                 Roo.lib.AnimMgr.registerElement(this);
3321             };
3322
3323
3324             this.stop = function(finish) {
3325                 if (finish) {
3326                     this.currentFrame = this.totalFrames;
3327                     this._onTween.fire();
3328                 }
3329                 Roo.lib.AnimMgr.stop(this);
3330             };
3331
3332             var onStart = function() {
3333                 this.onStart.fire();
3334
3335                 this.runtimeAttributes = {};
3336                 for (var attr in this.attributes) {
3337                     this.setRuntimeAttribute(attr);
3338                 }
3339
3340                 isAnimated = true;
3341                 actualFrames = 0;
3342                 startTime = new Date();
3343             };
3344
3345
3346             var onTween = function() {
3347                 var data = {
3348                     duration: new Date() - this.getStartTime(),
3349                     currentFrame: this.currentFrame
3350                 };
3351
3352                 data.toString = function() {
3353                     return (
3354                             'duration: ' + data.duration +
3355                             ', currentFrame: ' + data.currentFrame
3356                             );
3357                 };
3358
3359                 this.onTween.fire(data);
3360
3361                 var runtimeAttributes = this.runtimeAttributes;
3362
3363                 for (var attr in runtimeAttributes) {
3364                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3365                 }
3366
3367                 actualFrames += 1;
3368             };
3369
3370             var onComplete = function() {
3371                 var actual_duration = (new Date() - startTime) / 1000 ;
3372
3373                 var data = {
3374                     duration: actual_duration,
3375                     frames: actualFrames,
3376                     fps: actualFrames / actual_duration
3377                 };
3378
3379                 data.toString = function() {
3380                     return (
3381                             'duration: ' + data.duration +
3382                             ', frames: ' + data.frames +
3383                             ', fps: ' + data.fps
3384                             );
3385                 };
3386
3387                 isAnimated = false;
3388                 actualFrames = 0;
3389                 this.onComplete.fire(data);
3390             };
3391
3392
3393             this._onStart = new Roo.util.Event(this);
3394             this.onStart = new Roo.util.Event(this);
3395             this.onTween = new Roo.util.Event(this);
3396             this._onTween = new Roo.util.Event(this);
3397             this.onComplete = new Roo.util.Event(this);
3398             this._onComplete = new Roo.util.Event(this);
3399             this._onStart.addListener(onStart);
3400             this._onTween.addListener(onTween);
3401             this._onComplete.addListener(onComplete);
3402         }
3403     };
3404 })();
3405 /*
3406  * Portions of this file are based on pieces of Yahoo User Interface Library
3407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3408  * YUI licensed under the BSD License:
3409  * http://developer.yahoo.net/yui/license.txt
3410  * <script type="text/javascript">
3411  *
3412  */
3413
3414 Roo.lib.AnimMgr = new function() {
3415
3416     var thread = null;
3417
3418
3419     var queue = [];
3420
3421
3422     var tweenCount = 0;
3423
3424
3425     this.fps = 1000;
3426
3427
3428     this.delay = 1;
3429
3430
3431     this.registerElement = function(tween) {
3432         queue[queue.length] = tween;
3433         tweenCount += 1;
3434         tween._onStart.fire();
3435         this.start();
3436     };
3437
3438
3439     this.unRegister = function(tween, index) {
3440         tween._onComplete.fire();
3441         index = index || getIndex(tween);
3442         if (index != -1) {
3443             queue.splice(index, 1);
3444         }
3445
3446         tweenCount -= 1;
3447         if (tweenCount <= 0) {
3448             this.stop();
3449         }
3450     };
3451
3452
3453     this.start = function() {
3454         if (thread === null) {
3455             thread = setInterval(this.run, this.delay);
3456         }
3457     };
3458
3459
3460     this.stop = function(tween) {
3461         if (!tween) {
3462             clearInterval(thread);
3463
3464             for (var i = 0, len = queue.length; i < len; ++i) {
3465                 if (queue[0].isAnimated()) {
3466                     this.unRegister(queue[0], 0);
3467                 }
3468             }
3469
3470             queue = [];
3471             thread = null;
3472             tweenCount = 0;
3473         }
3474         else {
3475             this.unRegister(tween);
3476         }
3477     };
3478
3479
3480     this.run = function() {
3481         for (var i = 0, len = queue.length; i < len; ++i) {
3482             var tween = queue[i];
3483             if (!tween || !tween.isAnimated()) {
3484                 continue;
3485             }
3486
3487             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3488             {
3489                 tween.currentFrame += 1;
3490
3491                 if (tween.useSeconds) {
3492                     correctFrame(tween);
3493                 }
3494                 tween._onTween.fire();
3495             }
3496             else {
3497                 Roo.lib.AnimMgr.stop(tween, i);
3498             }
3499         }
3500     };
3501
3502     var getIndex = function(anim) {
3503         for (var i = 0, len = queue.length; i < len; ++i) {
3504             if (queue[i] == anim) {
3505                 return i;
3506             }
3507         }
3508         return -1;
3509     };
3510
3511
3512     var correctFrame = function(tween) {
3513         var frames = tween.totalFrames;
3514         var frame = tween.currentFrame;
3515         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3516         var elapsed = (new Date() - tween.getStartTime());
3517         var tweak = 0;
3518
3519         if (elapsed < tween.duration * 1000) {
3520             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3521         } else {
3522             tweak = frames - (frame + 1);
3523         }
3524         if (tweak > 0 && isFinite(tweak)) {
3525             if (tween.currentFrame + tweak >= frames) {
3526                 tweak = frames - (frame + 1);
3527             }
3528
3529             tween.currentFrame += tweak;
3530         }
3531     };
3532 };
3533
3534     /*
3535  * Portions of this file are based on pieces of Yahoo User Interface Library
3536  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3537  * YUI licensed under the BSD License:
3538  * http://developer.yahoo.net/yui/license.txt
3539  * <script type="text/javascript">
3540  *
3541  */
3542 Roo.lib.Bezier = new function() {
3543
3544         this.getPosition = function(points, t) {
3545             var n = points.length;
3546             var tmp = [];
3547
3548             for (var i = 0; i < n; ++i) {
3549                 tmp[i] = [points[i][0], points[i][1]];
3550             }
3551
3552             for (var j = 1; j < n; ++j) {
3553                 for (i = 0; i < n - j; ++i) {
3554                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3555                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3556                 }
3557             }
3558
3559             return [ tmp[0][0], tmp[0][1] ];
3560
3561         };
3562     };/*
3563  * Portions of this file are based on pieces of Yahoo User Interface Library
3564  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3565  * YUI licensed under the BSD License:
3566  * http://developer.yahoo.net/yui/license.txt
3567  * <script type="text/javascript">
3568  *
3569  */
3570 (function() {
3571
3572     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3573         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3574     };
3575
3576     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3577
3578     var fly = Roo.lib.AnimBase.fly;
3579     var Y = Roo.lib;
3580     var superclass = Y.ColorAnim.superclass;
3581     var proto = Y.ColorAnim.prototype;
3582
3583     proto.toString = function() {
3584         var el = this.getEl();
3585         var id = el.id || el.tagName;
3586         return ("ColorAnim " + id);
3587     };
3588
3589     proto.patterns.color = /color$/i;
3590     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3591     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3592     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3593     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3594
3595
3596     proto.parseColor = function(s) {
3597         if (s.length == 3) {
3598             return s;
3599         }
3600
3601         var c = this.patterns.hex.exec(s);
3602         if (c && c.length == 4) {
3603             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3604         }
3605
3606         c = this.patterns.rgb.exec(s);
3607         if (c && c.length == 4) {
3608             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3609         }
3610
3611         c = this.patterns.hex3.exec(s);
3612         if (c && c.length == 4) {
3613             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3614         }
3615
3616         return null;
3617     };
3618     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3619     proto.getAttribute = function(attr) {
3620         var el = this.getEl();
3621         if (this.patterns.color.test(attr)) {
3622             var val = fly(el).getStyle(attr);
3623
3624             if (this.patterns.transparent.test(val)) {
3625                 var parent = el.parentNode;
3626                 val = fly(parent).getStyle(attr);
3627
3628                 while (parent && this.patterns.transparent.test(val)) {
3629                     parent = parent.parentNode;
3630                     val = fly(parent).getStyle(attr);
3631                     if (parent.tagName.toUpperCase() == 'HTML') {
3632                         val = '#fff';
3633                     }
3634                 }
3635             }
3636         } else {
3637             val = superclass.getAttribute.call(this, attr);
3638         }
3639
3640         return val;
3641     };
3642     proto.getAttribute = function(attr) {
3643         var el = this.getEl();
3644         if (this.patterns.color.test(attr)) {
3645             var val = fly(el).getStyle(attr);
3646
3647             if (this.patterns.transparent.test(val)) {
3648                 var parent = el.parentNode;
3649                 val = fly(parent).getStyle(attr);
3650
3651                 while (parent && this.patterns.transparent.test(val)) {
3652                     parent = parent.parentNode;
3653                     val = fly(parent).getStyle(attr);
3654                     if (parent.tagName.toUpperCase() == 'HTML') {
3655                         val = '#fff';
3656                     }
3657                 }
3658             }
3659         } else {
3660             val = superclass.getAttribute.call(this, attr);
3661         }
3662
3663         return val;
3664     };
3665
3666     proto.doMethod = function(attr, start, end) {
3667         var val;
3668
3669         if (this.patterns.color.test(attr)) {
3670             val = [];
3671             for (var i = 0, len = start.length; i < len; ++i) {
3672                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3673             }
3674
3675             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3676         }
3677         else {
3678             val = superclass.doMethod.call(this, attr, start, end);
3679         }
3680
3681         return val;
3682     };
3683
3684     proto.setRuntimeAttribute = function(attr) {
3685         superclass.setRuntimeAttribute.call(this, attr);
3686
3687         if (this.patterns.color.test(attr)) {
3688             var attributes = this.attributes;
3689             var start = this.parseColor(this.runtimeAttributes[attr].start);
3690             var end = this.parseColor(this.runtimeAttributes[attr].end);
3691
3692             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3693                 end = this.parseColor(attributes[attr].by);
3694
3695                 for (var i = 0, len = start.length; i < len; ++i) {
3696                     end[i] = start[i] + end[i];
3697                 }
3698             }
3699
3700             this.runtimeAttributes[attr].start = start;
3701             this.runtimeAttributes[attr].end = end;
3702         }
3703     };
3704 })();
3705
3706 /*
3707  * Portions of this file are based on pieces of Yahoo User Interface Library
3708  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3709  * YUI licensed under the BSD License:
3710  * http://developer.yahoo.net/yui/license.txt
3711  * <script type="text/javascript">
3712  *
3713  */
3714 Roo.lib.Easing = {
3715
3716
3717     easeNone: function (t, b, c, d) {
3718         return c * t / d + b;
3719     },
3720
3721
3722     easeIn: function (t, b, c, d) {
3723         return c * (t /= d) * t + b;
3724     },
3725
3726
3727     easeOut: function (t, b, c, d) {
3728         return -c * (t /= d) * (t - 2) + b;
3729     },
3730
3731
3732     easeBoth: function (t, b, c, d) {
3733         if ((t /= d / 2) < 1) {
3734             return c / 2 * t * t + b;
3735         }
3736
3737         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3738     },
3739
3740
3741     easeInStrong: function (t, b, c, d) {
3742         return c * (t /= d) * t * t * t + b;
3743     },
3744
3745
3746     easeOutStrong: function (t, b, c, d) {
3747         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3748     },
3749
3750
3751     easeBothStrong: function (t, b, c, d) {
3752         if ((t /= d / 2) < 1) {
3753             return c / 2 * t * t * t * t + b;
3754         }
3755
3756         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3757     },
3758
3759
3760
3761     elasticIn: function (t, b, c, d, a, p) {
3762         if (t == 0) {
3763             return b;
3764         }
3765         if ((t /= d) == 1) {
3766             return b + c;
3767         }
3768         if (!p) {
3769             p = d * .3;
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3781     },
3782
3783
3784     elasticOut: function (t, b, c, d, a, p) {
3785         if (t == 0) {
3786             return b;
3787         }
3788         if ((t /= d) == 1) {
3789             return b + c;
3790         }
3791         if (!p) {
3792             p = d * .3;
3793         }
3794
3795         if (!a || a < Math.abs(c)) {
3796             a = c;
3797             var s = p / 4;
3798         }
3799         else {
3800             var s = p / (2 * Math.PI) * Math.asin(c / a);
3801         }
3802
3803         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3804     },
3805
3806
3807     elasticBoth: function (t, b, c, d, a, p) {
3808         if (t == 0) {
3809             return b;
3810         }
3811
3812         if ((t /= d / 2) == 2) {
3813             return b + c;
3814         }
3815
3816         if (!p) {
3817             p = d * (.3 * 1.5);
3818         }
3819
3820         if (!a || a < Math.abs(c)) {
3821             a = c;
3822             var s = p / 4;
3823         }
3824         else {
3825             var s = p / (2 * Math.PI) * Math.asin(c / a);
3826         }
3827
3828         if (t < 1) {
3829             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3830                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3831         }
3832         return a * Math.pow(2, -10 * (t -= 1)) *
3833                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3834     },
3835
3836
3837
3838     backIn: function (t, b, c, d, s) {
3839         if (typeof s == 'undefined') {
3840             s = 1.70158;
3841         }
3842         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3843     },
3844
3845
3846     backOut: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3851     },
3852
3853
3854     backBoth: function (t, b, c, d, s) {
3855         if (typeof s == 'undefined') {
3856             s = 1.70158;
3857         }
3858
3859         if ((t /= d / 2 ) < 1) {
3860             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3861         }
3862         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3863     },
3864
3865
3866     bounceIn: function (t, b, c, d) {
3867         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3868     },
3869
3870
3871     bounceOut: function (t, b, c, d) {
3872         if ((t /= d) < (1 / 2.75)) {
3873             return c * (7.5625 * t * t) + b;
3874         } else if (t < (2 / 2.75)) {
3875             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3876         } else if (t < (2.5 / 2.75)) {
3877             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3878         }
3879         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3880     },
3881
3882
3883     bounceBoth: function (t, b, c, d) {
3884         if (t < d / 2) {
3885             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3886         }
3887         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3888     }
3889 };/*
3890  * Portions of this file are based on pieces of Yahoo User Interface Library
3891  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3892  * YUI licensed under the BSD License:
3893  * http://developer.yahoo.net/yui/license.txt
3894  * <script type="text/javascript">
3895  *
3896  */
3897     (function() {
3898         Roo.lib.Motion = function(el, attributes, duration, method) {
3899             if (el) {
3900                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3901             }
3902         };
3903
3904         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3905
3906
3907         var Y = Roo.lib;
3908         var superclass = Y.Motion.superclass;
3909         var proto = Y.Motion.prototype;
3910
3911         proto.toString = function() {
3912             var el = this.getEl();
3913             var id = el.id || el.tagName;
3914             return ("Motion " + id);
3915         };
3916
3917         proto.patterns.points = /^points$/i;
3918
3919         proto.setAttribute = function(attr, val, unit) {
3920             if (this.patterns.points.test(attr)) {
3921                 unit = unit || 'px';
3922                 superclass.setAttribute.call(this, 'left', val[0], unit);
3923                 superclass.setAttribute.call(this, 'top', val[1], unit);
3924             } else {
3925                 superclass.setAttribute.call(this, attr, val, unit);
3926             }
3927         };
3928
3929         proto.getAttribute = function(attr) {
3930             if (this.patterns.points.test(attr)) {
3931                 var val = [
3932                         superclass.getAttribute.call(this, 'left'),
3933                         superclass.getAttribute.call(this, 'top')
3934                         ];
3935             } else {
3936                 val = superclass.getAttribute.call(this, attr);
3937             }
3938
3939             return val;
3940         };
3941
3942         proto.doMethod = function(attr, start, end) {
3943             var val = null;
3944
3945             if (this.patterns.points.test(attr)) {
3946                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3947                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3948             } else {
3949                 val = superclass.doMethod.call(this, attr, start, end);
3950             }
3951             return val;
3952         };
3953
3954         proto.setRuntimeAttribute = function(attr) {
3955             if (this.patterns.points.test(attr)) {
3956                 var el = this.getEl();
3957                 var attributes = this.attributes;
3958                 var start;
3959                 var control = attributes['points']['control'] || [];
3960                 var end;
3961                 var i, len;
3962
3963                 if (control.length > 0 && !(control[0] instanceof Array)) {
3964                     control = [control];
3965                 } else {
3966                     var tmp = [];
3967                     for (i = 0,len = control.length; i < len; ++i) {
3968                         tmp[i] = control[i];
3969                     }
3970                     control = tmp;
3971                 }
3972
3973                 Roo.fly(el).position();
3974
3975                 if (isset(attributes['points']['from'])) {
3976                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3977                 }
3978                 else {
3979                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3980                 }
3981
3982                 start = this.getAttribute('points');
3983
3984
3985                 if (isset(attributes['points']['to'])) {
3986                     end = translateValues.call(this, attributes['points']['to'], start);
3987
3988                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3989                     for (i = 0,len = control.length; i < len; ++i) {
3990                         control[i] = translateValues.call(this, control[i], start);
3991                     }
3992
3993
3994                 } else if (isset(attributes['points']['by'])) {
3995                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3996
3997                     for (i = 0,len = control.length; i < len; ++i) {
3998                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3999                     }
4000                 }
4001
4002                 this.runtimeAttributes[attr] = [start];
4003
4004                 if (control.length > 0) {
4005                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4006                 }
4007
4008                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4009             }
4010             else {
4011                 superclass.setRuntimeAttribute.call(this, attr);
4012             }
4013         };
4014
4015         var translateValues = function(val, start) {
4016             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4017             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4018
4019             return val;
4020         };
4021
4022         var isset = function(prop) {
4023             return (typeof prop !== 'undefined');
4024         };
4025     })();
4026 /*
4027  * Portions of this file are based on pieces of Yahoo User Interface Library
4028  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4029  * YUI licensed under the BSD License:
4030  * http://developer.yahoo.net/yui/license.txt
4031  * <script type="text/javascript">
4032  *
4033  */
4034     (function() {
4035         Roo.lib.Scroll = function(el, attributes, duration, method) {
4036             if (el) {
4037                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4038             }
4039         };
4040
4041         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4042
4043
4044         var Y = Roo.lib;
4045         var superclass = Y.Scroll.superclass;
4046         var proto = Y.Scroll.prototype;
4047
4048         proto.toString = function() {
4049             var el = this.getEl();
4050             var id = el.id || el.tagName;
4051             return ("Scroll " + id);
4052         };
4053
4054         proto.doMethod = function(attr, start, end) {
4055             var val = null;
4056
4057             if (attr == 'scroll') {
4058                 val = [
4059                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4060                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4061                         ];
4062
4063             } else {
4064                 val = superclass.doMethod.call(this, attr, start, end);
4065             }
4066             return val;
4067         };
4068
4069         proto.getAttribute = function(attr) {
4070             var val = null;
4071             var el = this.getEl();
4072
4073             if (attr == 'scroll') {
4074                 val = [ el.scrollLeft, el.scrollTop ];
4075             } else {
4076                 val = superclass.getAttribute.call(this, attr);
4077             }
4078
4079             return val;
4080         };
4081
4082         proto.setAttribute = function(attr, val, unit) {
4083             var el = this.getEl();
4084
4085             if (attr == 'scroll') {
4086                 el.scrollLeft = val[0];
4087                 el.scrollTop = val[1];
4088             } else {
4089                 superclass.setAttribute.call(this, attr, val, unit);
4090             }
4091         };
4092     })();
4093 /*
4094  * Based on:
4095  * Ext JS Library 1.1.1
4096  * Copyright(c) 2006-2007, Ext JS, LLC.
4097  *
4098  * Originally Released Under LGPL - original licence link has changed is not relivant.
4099  *
4100  * Fork - LGPL
4101  * <script type="text/javascript">
4102  */
4103
4104
4105 // nasty IE9 hack - what a pile of crap that is..
4106
4107  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4108     Range.prototype.createContextualFragment = function (html) {
4109         var doc = window.document;
4110         var container = doc.createElement("div");
4111         container.innerHTML = html;
4112         var frag = doc.createDocumentFragment(), n;
4113         while ((n = container.firstChild)) {
4114             frag.appendChild(n);
4115         }
4116         return frag;
4117     };
4118 }
4119
4120 /**
4121  * @class Roo.DomHelper
4122  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4123  * 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>.
4124  * @singleton
4125  */
4126 Roo.DomHelper = function(){
4127     var tempTableEl = null;
4128     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4129     var tableRe = /^table|tbody|tr|td$/i;
4130     var xmlns = {};
4131     // build as innerHTML where available
4132     /** @ignore */
4133     var createHtml = function(o){
4134         if(typeof o == 'string'){
4135             return o;
4136         }
4137         var b = "";
4138         if(!o.tag){
4139             o.tag = "div";
4140         }
4141         b += "<" + o.tag;
4142         for(var attr in o){
4143             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4144             if(attr == "style"){
4145                 var s = o["style"];
4146                 if(typeof s == "function"){
4147                     s = s.call();
4148                 }
4149                 if(typeof s == "string"){
4150                     b += ' style="' + s + '"';
4151                 }else if(typeof s == "object"){
4152                     b += ' style="';
4153                     for(var key in s){
4154                         if(typeof s[key] != "function"){
4155                             b += key + ":" + s[key] + ";";
4156                         }
4157                     }
4158                     b += '"';
4159                 }
4160             }else{
4161                 if(attr == "cls"){
4162                     b += ' class="' + o["cls"] + '"';
4163                 }else if(attr == "htmlFor"){
4164                     b += ' for="' + o["htmlFor"] + '"';
4165                 }else{
4166                     b += " " + attr + '="' + o[attr] + '"';
4167                 }
4168             }
4169         }
4170         if(emptyTags.test(o.tag)){
4171             b += "/>";
4172         }else{
4173             b += ">";
4174             var cn = o.children || o.cn;
4175             if(cn){
4176                 //http://bugs.kde.org/show_bug.cgi?id=71506
4177                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4178                     for(var i = 0, len = cn.length; i < len; i++) {
4179                         b += createHtml(cn[i], b);
4180                     }
4181                 }else{
4182                     b += createHtml(cn, b);
4183                 }
4184             }
4185             if(o.html){
4186                 b += o.html;
4187             }
4188             b += "</" + o.tag + ">";
4189         }
4190         return b;
4191     };
4192
4193     // build as dom
4194     /** @ignore */
4195     var createDom = function(o, parentNode){
4196          
4197         // defininition craeted..
4198         var ns = false;
4199         if (o.ns && o.ns != 'html') {
4200                
4201             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4202                 xmlns[o.ns] = o.xmlns;
4203                 ns = o.xmlns;
4204             }
4205             if (typeof(xmlns[o.ns]) == 'undefined') {
4206                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4207             }
4208             ns = xmlns[o.ns];
4209         }
4210         
4211         
4212         if (typeof(o) == 'string') {
4213             return parentNode.appendChild(document.createTextNode(o));
4214         }
4215         o.tag = o.tag || div;
4216         if (o.ns && Roo.isIE) {
4217             ns = false;
4218             o.tag = o.ns + ':' + o.tag;
4219             
4220         }
4221         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4222         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4223         for(var attr in o){
4224             
4225             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4226                     attr == "style" || typeof o[attr] == "function") continue;
4227                     
4228             if(attr=="cls" && Roo.isIE){
4229                 el.className = o["cls"];
4230             }else{
4231                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4232                 else el[attr] = o[attr];
4233             }
4234         }
4235         Roo.DomHelper.applyStyles(el, o.style);
4236         var cn = o.children || o.cn;
4237         if(cn){
4238             //http://bugs.kde.org/show_bug.cgi?id=71506
4239              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4240                 for(var i = 0, len = cn.length; i < len; i++) {
4241                     createDom(cn[i], el);
4242                 }
4243             }else{
4244                 createDom(cn, el);
4245             }
4246         }
4247         if(o.html){
4248             el.innerHTML = o.html;
4249         }
4250         if(parentNode){
4251            parentNode.appendChild(el);
4252         }
4253         return el;
4254     };
4255
4256     var ieTable = function(depth, s, h, e){
4257         tempTableEl.innerHTML = [s, h, e].join('');
4258         var i = -1, el = tempTableEl;
4259         while(++i < depth){
4260             el = el.firstChild;
4261         }
4262         return el;
4263     };
4264
4265     // kill repeat to save bytes
4266     var ts = '<table>',
4267         te = '</table>',
4268         tbs = ts+'<tbody>',
4269         tbe = '</tbody>'+te,
4270         trs = tbs + '<tr>',
4271         tre = '</tr>'+tbe;
4272
4273     /**
4274      * @ignore
4275      * Nasty code for IE's broken table implementation
4276      */
4277     var insertIntoTable = function(tag, where, el, html){
4278         if(!tempTableEl){
4279             tempTableEl = document.createElement('div');
4280         }
4281         var node;
4282         var before = null;
4283         if(tag == 'td'){
4284             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4285                 return;
4286             }
4287             if(where == 'beforebegin'){
4288                 before = el;
4289                 el = el.parentNode;
4290             } else{
4291                 before = el.nextSibling;
4292                 el = el.parentNode;
4293             }
4294             node = ieTable(4, trs, html, tre);
4295         }
4296         else if(tag == 'tr'){
4297             if(where == 'beforebegin'){
4298                 before = el;
4299                 el = el.parentNode;
4300                 node = ieTable(3, tbs, html, tbe);
4301             } else if(where == 'afterend'){
4302                 before = el.nextSibling;
4303                 el = el.parentNode;
4304                 node = ieTable(3, tbs, html, tbe);
4305             } else{ // INTO a TR
4306                 if(where == 'afterbegin'){
4307                     before = el.firstChild;
4308                 }
4309                 node = ieTable(4, trs, html, tre);
4310             }
4311         } else if(tag == 'tbody'){
4312             if(where == 'beforebegin'){
4313                 before = el;
4314                 el = el.parentNode;
4315                 node = ieTable(2, ts, html, te);
4316             } else if(where == 'afterend'){
4317                 before = el.nextSibling;
4318                 el = el.parentNode;
4319                 node = ieTable(2, ts, html, te);
4320             } else{
4321                 if(where == 'afterbegin'){
4322                     before = el.firstChild;
4323                 }
4324                 node = ieTable(3, tbs, html, tbe);
4325             }
4326         } else{ // TABLE
4327             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4328                 return;
4329             }
4330             if(where == 'afterbegin'){
4331                 before = el.firstChild;
4332             }
4333             node = ieTable(2, ts, html, te);
4334         }
4335         el.insertBefore(node, before);
4336         return node;
4337     };
4338
4339     return {
4340     /** True to force the use of DOM instead of html fragments @type Boolean */
4341     useDom : false,
4342
4343     /**
4344      * Returns the markup for the passed Element(s) config
4345      * @param {Object} o The Dom object spec (and children)
4346      * @return {String}
4347      */
4348     markup : function(o){
4349         return createHtml(o);
4350     },
4351
4352     /**
4353      * Applies a style specification to an element
4354      * @param {String/HTMLElement} el The element to apply styles to
4355      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4356      * a function which returns such a specification.
4357      */
4358     applyStyles : function(el, styles){
4359         if(styles){
4360            el = Roo.fly(el);
4361            if(typeof styles == "string"){
4362                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4363                var matches;
4364                while ((matches = re.exec(styles)) != null){
4365                    el.setStyle(matches[1], matches[2]);
4366                }
4367            }else if (typeof styles == "object"){
4368                for (var style in styles){
4369                   el.setStyle(style, styles[style]);
4370                }
4371            }else if (typeof styles == "function"){
4372                 Roo.DomHelper.applyStyles(el, styles.call());
4373            }
4374         }
4375     },
4376
4377     /**
4378      * Inserts an HTML fragment into the Dom
4379      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4380      * @param {HTMLElement} el The context element
4381      * @param {String} html The HTML fragmenet
4382      * @return {HTMLElement} The new node
4383      */
4384     insertHtml : function(where, el, html){
4385         where = where.toLowerCase();
4386         if(el.insertAdjacentHTML){
4387             if(tableRe.test(el.tagName)){
4388                 var rs;
4389                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4390                     return rs;
4391                 }
4392             }
4393             switch(where){
4394                 case "beforebegin":
4395                     el.insertAdjacentHTML('BeforeBegin', html);
4396                     return el.previousSibling;
4397                 case "afterbegin":
4398                     el.insertAdjacentHTML('AfterBegin', html);
4399                     return el.firstChild;
4400                 case "beforeend":
4401                     el.insertAdjacentHTML('BeforeEnd', html);
4402                     return el.lastChild;
4403                 case "afterend":
4404                     el.insertAdjacentHTML('AfterEnd', html);
4405                     return el.nextSibling;
4406             }
4407             throw 'Illegal insertion point -> "' + where + '"';
4408         }
4409         var range = el.ownerDocument.createRange();
4410         var frag;
4411         switch(where){
4412              case "beforebegin":
4413                 range.setStartBefore(el);
4414                 frag = range.createContextualFragment(html);
4415                 el.parentNode.insertBefore(frag, el);
4416                 return el.previousSibling;
4417              case "afterbegin":
4418                 if(el.firstChild){
4419                     range.setStartBefore(el.firstChild);
4420                     frag = range.createContextualFragment(html);
4421                     el.insertBefore(frag, el.firstChild);
4422                     return el.firstChild;
4423                 }else{
4424                     el.innerHTML = html;
4425                     return el.firstChild;
4426                 }
4427             case "beforeend":
4428                 if(el.lastChild){
4429                     range.setStartAfter(el.lastChild);
4430                     frag = range.createContextualFragment(html);
4431                     el.appendChild(frag);
4432                     return el.lastChild;
4433                 }else{
4434                     el.innerHTML = html;
4435                     return el.lastChild;
4436                 }
4437             case "afterend":
4438                 range.setStartAfter(el);
4439                 frag = range.createContextualFragment(html);
4440                 el.parentNode.insertBefore(frag, el.nextSibling);
4441                 return el.nextSibling;
4442             }
4443             throw 'Illegal insertion point -> "' + where + '"';
4444     },
4445
4446     /**
4447      * Creates new Dom element(s) and inserts them before el
4448      * @param {String/HTMLElement/Element} el The context element
4449      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4450      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4451      * @return {HTMLElement/Roo.Element} The new node
4452      */
4453     insertBefore : function(el, o, returnElement){
4454         return this.doInsert(el, o, returnElement, "beforeBegin");
4455     },
4456
4457     /**
4458      * Creates new Dom element(s) and inserts them after el
4459      * @param {String/HTMLElement/Element} el The context element
4460      * @param {Object} o The Dom object spec (and children)
4461      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4462      * @return {HTMLElement/Roo.Element} The new node
4463      */
4464     insertAfter : function(el, o, returnElement){
4465         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them as the first child of el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertFirst : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "afterBegin");
4477     },
4478
4479     // private
4480     doInsert : function(el, o, returnElement, pos, sibling){
4481         el = Roo.getDom(el);
4482         var newNode;
4483         if(this.useDom || o.ns){
4484             newNode = createDom(o, null);
4485             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4486         }else{
4487             var html = createHtml(o);
4488             newNode = this.insertHtml(pos, el, html);
4489         }
4490         return returnElement ? Roo.get(newNode, true) : newNode;
4491     },
4492
4493     /**
4494      * Creates new Dom element(s) and appends them to el
4495      * @param {String/HTMLElement/Element} el The context element
4496      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4497      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4498      * @return {HTMLElement/Roo.Element} The new node
4499      */
4500     append : function(el, o, returnElement){
4501         el = Roo.getDom(el);
4502         var newNode;
4503         if(this.useDom || o.ns){
4504             newNode = createDom(o, null);
4505             el.appendChild(newNode);
4506         }else{
4507             var html = createHtml(o);
4508             newNode = this.insertHtml("beforeEnd", el, html);
4509         }
4510         return returnElement ? Roo.get(newNode, true) : newNode;
4511     },
4512
4513     /**
4514      * Creates new Dom element(s) and overwrites the contents of el with them
4515      * @param {String/HTMLElement/Element} el The context element
4516      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4517      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4518      * @return {HTMLElement/Roo.Element} The new node
4519      */
4520     overwrite : function(el, o, returnElement){
4521         el = Roo.getDom(el);
4522         if (o.ns) {
4523           
4524             while (el.childNodes.length) {
4525                 el.removeChild(el.firstChild);
4526             }
4527             createDom(o, el);
4528         } else {
4529             el.innerHTML = createHtml(o);   
4530         }
4531         
4532         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4533     },
4534
4535     /**
4536      * Creates a new Roo.DomHelper.Template from the Dom object spec
4537      * @param {Object} o The Dom object spec (and children)
4538      * @return {Roo.DomHelper.Template} The new template
4539      */
4540     createTemplate : function(o){
4541         var html = createHtml(o);
4542         return new Roo.Template(html);
4543     }
4544     };
4545 }();
4546 /*
4547  * Based on:
4548  * Ext JS Library 1.1.1
4549  * Copyright(c) 2006-2007, Ext JS, LLC.
4550  *
4551  * Originally Released Under LGPL - original licence link has changed is not relivant.
4552  *
4553  * Fork - LGPL
4554  * <script type="text/javascript">
4555  */
4556  
4557 /**
4558 * @class Roo.Template
4559 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4560 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4561 * Usage:
4562 <pre><code>
4563 var t = new Roo.Template({
4564     html :  '&lt;div name="{id}"&gt;' + 
4565         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4566         '&lt;/div&gt;',
4567     myformat: function (value, allValues) {
4568         return 'XX' + value;
4569     }
4570 });
4571 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4572 </code></pre>
4573 * For more information see this blog post with examples:
4574 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4575      - Create Elements using DOM, HTML fragments and Templates</a>. 
4576 * @constructor
4577 * @param {Object} cfg - Configuration object.
4578 */
4579 Roo.Template = function(cfg){
4580     // BC!
4581     if(cfg instanceof Array){
4582         cfg = cfg.join("");
4583     }else if(arguments.length > 1){
4584         cfg = Array.prototype.join.call(arguments, "");
4585     }
4586     
4587     
4588     if (typeof(cfg) == 'object') {
4589         Roo.apply(this,cfg)
4590     } else {
4591         // bc
4592         this.html = cfg;
4593     }
4594     if (this.url) {
4595         this.load();
4596     }
4597     
4598 };
4599 Roo.Template.prototype = {
4600     
4601     /**
4602      * @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..
4603      *                    it should be fixed so that template is observable...
4604      */
4605     url : false,
4606     /**
4607      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4608      */
4609     html : '',
4610     /**
4611      * Returns an HTML fragment of this template with the specified values applied.
4612      * @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'})
4613      * @return {String} The HTML fragment
4614      */
4615     applyTemplate : function(values){
4616         try {
4617            
4618             if(this.compiled){
4619                 return this.compiled(values);
4620             }
4621             var useF = this.disableFormats !== true;
4622             var fm = Roo.util.Format, tpl = this;
4623             var fn = function(m, name, format, args){
4624                 if(format && useF){
4625                     if(format.substr(0, 5) == "this."){
4626                         return tpl.call(format.substr(5), values[name], values);
4627                     }else{
4628                         if(args){
4629                             // quoted values are required for strings in compiled templates, 
4630                             // but for non compiled we need to strip them
4631                             // quoted reversed for jsmin
4632                             var re = /^\s*['"](.*)["']\s*$/;
4633                             args = args.split(',');
4634                             for(var i = 0, len = args.length; i < len; i++){
4635                                 args[i] = args[i].replace(re, "$1");
4636                             }
4637                             args = [values[name]].concat(args);
4638                         }else{
4639                             args = [values[name]];
4640                         }
4641                         return fm[format].apply(fm, args);
4642                     }
4643                 }else{
4644                     return values[name] !== undefined ? values[name] : "";
4645                 }
4646             };
4647             return this.html.replace(this.re, fn);
4648         } catch (e) {
4649             Roo.log(e);
4650             throw e;
4651         }
4652          
4653     },
4654     
4655     loading : false,
4656       
4657     load : function ()
4658     {
4659          
4660         if (this.loading) {
4661             return;
4662         }
4663         var _t = this;
4664         
4665         this.loading = true;
4666         this.compiled = false;
4667         
4668         var cx = new Roo.data.Connection();
4669         cx.request({
4670             url : this.url,
4671             method : 'GET',
4672             success : function (response) {
4673                 _t.loading = false;
4674                 _t.html = response.responseText;
4675                 _t.url = false;
4676                 _t.compile();
4677              },
4678             failure : function(response) {
4679                 Roo.log("Template failed to load from " + _t.url);
4680                 _t.loading = false;
4681             }
4682         });
4683     },
4684
4685     /**
4686      * Sets the HTML used as the template and optionally compiles it.
4687      * @param {String} html
4688      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4689      * @return {Roo.Template} this
4690      */
4691     set : function(html, compile){
4692         this.html = html;
4693         this.compiled = null;
4694         if(compile){
4695             this.compile();
4696         }
4697         return this;
4698     },
4699     
4700     /**
4701      * True to disable format functions (defaults to false)
4702      * @type Boolean
4703      */
4704     disableFormats : false,
4705     
4706     /**
4707     * The regular expression used to match template variables 
4708     * @type RegExp
4709     * @property 
4710     */
4711     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4712     
4713     /**
4714      * Compiles the template into an internal function, eliminating the RegEx overhead.
4715      * @return {Roo.Template} this
4716      */
4717     compile : function(){
4718         var fm = Roo.util.Format;
4719         var useF = this.disableFormats !== true;
4720         var sep = Roo.isGecko ? "+" : ",";
4721         var fn = function(m, name, format, args){
4722             if(format && useF){
4723                 args = args ? ',' + args : "";
4724                 if(format.substr(0, 5) != "this."){
4725                     format = "fm." + format + '(';
4726                 }else{
4727                     format = 'this.call("'+ format.substr(5) + '", ';
4728                     args = ", values";
4729                 }
4730             }else{
4731                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4732             }
4733             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4734         };
4735         var body;
4736         // branched to use + in gecko and [].join() in others
4737         if(Roo.isGecko){
4738             body = "this.compiled = function(values){ return '" +
4739                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4740                     "';};";
4741         }else{
4742             body = ["this.compiled = function(values){ return ['"];
4743             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4744             body.push("'].join('');};");
4745             body = body.join('');
4746         }
4747         /**
4748          * eval:var:values
4749          * eval:var:fm
4750          */
4751         eval(body);
4752         return this;
4753     },
4754     
4755     // private function used to call members
4756     call : function(fnName, value, allValues){
4757         return this[fnName](value, allValues);
4758     },
4759     
4760     /**
4761      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4762      * @param {String/HTMLElement/Roo.Element} el The context element
4763      * @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'})
4764      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4765      * @return {HTMLElement/Roo.Element} The new node or Element
4766      */
4767     insertFirst: function(el, values, returnElement){
4768         return this.doInsert('afterBegin', el, values, returnElement);
4769     },
4770
4771     /**
4772      * Applies the supplied values to the template and inserts the new node(s) before el.
4773      * @param {String/HTMLElement/Roo.Element} el The context element
4774      * @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'})
4775      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4776      * @return {HTMLElement/Roo.Element} The new node or Element
4777      */
4778     insertBefore: function(el, values, returnElement){
4779         return this.doInsert('beforeBegin', el, values, returnElement);
4780     },
4781
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) after el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @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'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertAfter : function(el, values, returnElement){
4790         return this.doInsert('afterEnd', el, values, returnElement);
4791     },
4792     
4793     /**
4794      * Applies the supplied values to the template and appends the new node(s) to el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @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'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     append : function(el, values, returnElement){
4801         return this.doInsert('beforeEnd', el, values, returnElement);
4802     },
4803
4804     doInsert : function(where, el, values, returnEl){
4805         el = Roo.getDom(el);
4806         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4807         return returnEl ? Roo.get(newNode, true) : newNode;
4808     },
4809
4810     /**
4811      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4812      * @param {String/HTMLElement/Roo.Element} el The context element
4813      * @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'})
4814      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4815      * @return {HTMLElement/Roo.Element} The new node or Element
4816      */
4817     overwrite : function(el, values, returnElement){
4818         el = Roo.getDom(el);
4819         el.innerHTML = this.applyTemplate(values);
4820         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4821     }
4822 };
4823 /**
4824  * Alias for {@link #applyTemplate}
4825  * @method
4826  */
4827 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4828
4829 // backwards compat
4830 Roo.DomHelper.Template = Roo.Template;
4831
4832 /**
4833  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4834  * @param {String/HTMLElement} el A DOM element or its id
4835  * @returns {Roo.Template} The created template
4836  * @static
4837  */
4838 Roo.Template.from = function(el){
4839     el = Roo.getDom(el);
4840     return new Roo.Template(el.value || el.innerHTML);
4841 };/*
4842  * Based on:
4843  * Ext JS Library 1.1.1
4844  * Copyright(c) 2006-2007, Ext JS, LLC.
4845  *
4846  * Originally Released Under LGPL - original licence link has changed is not relivant.
4847  *
4848  * Fork - LGPL
4849  * <script type="text/javascript">
4850  */
4851  
4852
4853 /*
4854  * This is code is also distributed under MIT license for use
4855  * with jQuery and prototype JavaScript libraries.
4856  */
4857 /**
4858  * @class Roo.DomQuery
4859 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).
4860 <p>
4861 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>
4862
4863 <p>
4864 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.
4865 </p>
4866 <h4>Element Selectors:</h4>
4867 <ul class="list">
4868     <li> <b>*</b> any element</li>
4869     <li> <b>E</b> an element with the tag E</li>
4870     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4871     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4872     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4873     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4874 </ul>
4875 <h4>Attribute Selectors:</h4>
4876 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4877 <ul class="list">
4878     <li> <b>E[foo]</b> has an attribute "foo"</li>
4879     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4880     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4881     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4882     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4883     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4884     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4885 </ul>
4886 <h4>Pseudo Classes:</h4>
4887 <ul class="list">
4888     <li> <b>E:first-child</b> E is the first child of its parent</li>
4889     <li> <b>E:last-child</b> E is the last child of its parent</li>
4890     <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>
4891     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4892     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4893     <li> <b>E:only-child</b> E is the only child of its parent</li>
4894     <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>
4895     <li> <b>E:first</b> the first E in the resultset</li>
4896     <li> <b>E:last</b> the last E in the resultset</li>
4897     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4898     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4899     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4900     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4901     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4902     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4903     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4904     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4905     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4906 </ul>
4907 <h4>CSS Value Selectors:</h4>
4908 <ul class="list">
4909     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4910     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4911     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4912     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4913     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4914     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4915 </ul>
4916  * @singleton
4917  */
4918 Roo.DomQuery = function(){
4919     var cache = {}, simpleCache = {}, valueCache = {};
4920     var nonSpace = /\S/;
4921     var trimRe = /^\s+|\s+$/g;
4922     var tplRe = /\{(\d+)\}/g;
4923     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4924     var tagTokenRe = /^(#)?([\w-\*]+)/;
4925     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4926
4927     function child(p, index){
4928         var i = 0;
4929         var n = p.firstChild;
4930         while(n){
4931             if(n.nodeType == 1){
4932                if(++i == index){
4933                    return n;
4934                }
4935             }
4936             n = n.nextSibling;
4937         }
4938         return null;
4939     };
4940
4941     function next(n){
4942         while((n = n.nextSibling) && n.nodeType != 1);
4943         return n;
4944     };
4945
4946     function prev(n){
4947         while((n = n.previousSibling) && n.nodeType != 1);
4948         return n;
4949     };
4950
4951     function children(d){
4952         var n = d.firstChild, ni = -1;
4953             while(n){
4954                 var nx = n.nextSibling;
4955                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4956                     d.removeChild(n);
4957                 }else{
4958                     n.nodeIndex = ++ni;
4959                 }
4960                 n = nx;
4961             }
4962             return this;
4963         };
4964
4965     function byClassName(c, a, v){
4966         if(!v){
4967             return c;
4968         }
4969         var r = [], ri = -1, cn;
4970         for(var i = 0, ci; ci = c[i]; i++){
4971             if((' '+ci.className+' ').indexOf(v) != -1){
4972                 r[++ri] = ci;
4973             }
4974         }
4975         return r;
4976     };
4977
4978     function attrValue(n, attr){
4979         if(!n.tagName && typeof n.length != "undefined"){
4980             n = n[0];
4981         }
4982         if(!n){
4983             return null;
4984         }
4985         if(attr == "for"){
4986             return n.htmlFor;
4987         }
4988         if(attr == "class" || attr == "className"){
4989             return n.className;
4990         }
4991         return n.getAttribute(attr) || n[attr];
4992
4993     };
4994
4995     function getNodes(ns, mode, tagName){
4996         var result = [], ri = -1, cs;
4997         if(!ns){
4998             return result;
4999         }
5000         tagName = tagName || "*";
5001         if(typeof ns.getElementsByTagName != "undefined"){
5002             ns = [ns];
5003         }
5004         if(!mode){
5005             for(var i = 0, ni; ni = ns[i]; i++){
5006                 cs = ni.getElementsByTagName(tagName);
5007                 for(var j = 0, ci; ci = cs[j]; j++){
5008                     result[++ri] = ci;
5009                 }
5010             }
5011         }else if(mode == "/" || mode == ">"){
5012             var utag = tagName.toUpperCase();
5013             for(var i = 0, ni, cn; ni = ns[i]; i++){
5014                 cn = ni.children || ni.childNodes;
5015                 for(var j = 0, cj; cj = cn[j]; j++){
5016                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5017                         result[++ri] = cj;
5018                     }
5019                 }
5020             }
5021         }else if(mode == "+"){
5022             var utag = tagName.toUpperCase();
5023             for(var i = 0, n; n = ns[i]; i++){
5024                 while((n = n.nextSibling) && n.nodeType != 1);
5025                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5026                     result[++ri] = n;
5027                 }
5028             }
5029         }else if(mode == "~"){
5030             for(var i = 0, n; n = ns[i]; i++){
5031                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5032                 if(n){
5033                     result[++ri] = n;
5034                 }
5035             }
5036         }
5037         return result;
5038     };
5039
5040     function concat(a, b){
5041         if(b.slice){
5042             return a.concat(b);
5043         }
5044         for(var i = 0, l = b.length; i < l; i++){
5045             a[a.length] = b[i];
5046         }
5047         return a;
5048     }
5049
5050     function byTag(cs, tagName){
5051         if(cs.tagName || cs == document){
5052             cs = [cs];
5053         }
5054         if(!tagName){
5055             return cs;
5056         }
5057         var r = [], ri = -1;
5058         tagName = tagName.toLowerCase();
5059         for(var i = 0, ci; ci = cs[i]; i++){
5060             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5061                 r[++ri] = ci;
5062             }
5063         }
5064         return r;
5065     };
5066
5067     function byId(cs, attr, id){
5068         if(cs.tagName || cs == document){
5069             cs = [cs];
5070         }
5071         if(!id){
5072             return cs;
5073         }
5074         var r = [], ri = -1;
5075         for(var i = 0,ci; ci = cs[i]; i++){
5076             if(ci && ci.id == id){
5077                 r[++ri] = ci;
5078                 return r;
5079             }
5080         }
5081         return r;
5082     };
5083
5084     function byAttribute(cs, attr, value, op, custom){
5085         var r = [], ri = -1, st = custom=="{";
5086         var f = Roo.DomQuery.operators[op];
5087         for(var i = 0, ci; ci = cs[i]; i++){
5088             var a;
5089             if(st){
5090                 a = Roo.DomQuery.getStyle(ci, attr);
5091             }
5092             else if(attr == "class" || attr == "className"){
5093                 a = ci.className;
5094             }else if(attr == "for"){
5095                 a = ci.htmlFor;
5096             }else if(attr == "href"){
5097                 a = ci.getAttribute("href", 2);
5098             }else{
5099                 a = ci.getAttribute(attr);
5100             }
5101             if((f && f(a, value)) || (!f && a)){
5102                 r[++ri] = ci;
5103             }
5104         }
5105         return r;
5106     };
5107
5108     function byPseudo(cs, name, value){
5109         return Roo.DomQuery.pseudos[name](cs, value);
5110     };
5111
5112     // This is for IE MSXML which does not support expandos.
5113     // IE runs the same speed using setAttribute, however FF slows way down
5114     // and Safari completely fails so they need to continue to use expandos.
5115     var isIE = window.ActiveXObject ? true : false;
5116
5117     // this eval is stop the compressor from
5118     // renaming the variable to something shorter
5119     
5120     /** eval:var:batch */
5121     var batch = 30803; 
5122
5123     var key = 30803;
5124
5125     function nodupIEXml(cs){
5126         var d = ++key;
5127         cs[0].setAttribute("_nodup", d);
5128         var r = [cs[0]];
5129         for(var i = 1, len = cs.length; i < len; i++){
5130             var c = cs[i];
5131             if(!c.getAttribute("_nodup") != d){
5132                 c.setAttribute("_nodup", d);
5133                 r[r.length] = c;
5134             }
5135         }
5136         for(var i = 0, len = cs.length; i < len; i++){
5137             cs[i].removeAttribute("_nodup");
5138         }
5139         return r;
5140     }
5141
5142     function nodup(cs){
5143         if(!cs){
5144             return [];
5145         }
5146         var len = cs.length, c, i, r = cs, cj, ri = -1;
5147         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5148             return cs;
5149         }
5150         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5151             return nodupIEXml(cs);
5152         }
5153         var d = ++key;
5154         cs[0]._nodup = d;
5155         for(i = 1; c = cs[i]; i++){
5156             if(c._nodup != d){
5157                 c._nodup = d;
5158             }else{
5159                 r = [];
5160                 for(var j = 0; j < i; j++){
5161                     r[++ri] = cs[j];
5162                 }
5163                 for(j = i+1; cj = cs[j]; j++){
5164                     if(cj._nodup != d){
5165                         cj._nodup = d;
5166                         r[++ri] = cj;
5167                     }
5168                 }
5169                 return r;
5170             }
5171         }
5172         return r;
5173     }
5174
5175     function quickDiffIEXml(c1, c2){
5176         var d = ++key;
5177         for(var i = 0, len = c1.length; i < len; i++){
5178             c1[i].setAttribute("_qdiff", d);
5179         }
5180         var r = [];
5181         for(var i = 0, len = c2.length; i < len; i++){
5182             if(c2[i].getAttribute("_qdiff") != d){
5183                 r[r.length] = c2[i];
5184             }
5185         }
5186         for(var i = 0, len = c1.length; i < len; i++){
5187            c1[i].removeAttribute("_qdiff");
5188         }
5189         return r;
5190     }
5191
5192     function quickDiff(c1, c2){
5193         var len1 = c1.length;
5194         if(!len1){
5195             return c2;
5196         }
5197         if(isIE && c1[0].selectSingleNode){
5198             return quickDiffIEXml(c1, c2);
5199         }
5200         var d = ++key;
5201         for(var i = 0; i < len1; i++){
5202             c1[i]._qdiff = d;
5203         }
5204         var r = [];
5205         for(var i = 0, len = c2.length; i < len; i++){
5206             if(c2[i]._qdiff != d){
5207                 r[r.length] = c2[i];
5208             }
5209         }
5210         return r;
5211     }
5212
5213     function quickId(ns, mode, root, id){
5214         if(ns == root){
5215            var d = root.ownerDocument || root;
5216            return d.getElementById(id);
5217         }
5218         ns = getNodes(ns, mode, "*");
5219         return byId(ns, null, id);
5220     }
5221
5222     return {
5223         getStyle : function(el, name){
5224             return Roo.fly(el).getStyle(name);
5225         },
5226         /**
5227          * Compiles a selector/xpath query into a reusable function. The returned function
5228          * takes one parameter "root" (optional), which is the context node from where the query should start.
5229          * @param {String} selector The selector/xpath query
5230          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5231          * @return {Function}
5232          */
5233         compile : function(path, type){
5234             type = type || "select";
5235             
5236             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5237             var q = path, mode, lq;
5238             var tk = Roo.DomQuery.matchers;
5239             var tklen = tk.length;
5240             var mm;
5241
5242             // accept leading mode switch
5243             var lmode = q.match(modeRe);
5244             if(lmode && lmode[1]){
5245                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5246                 q = q.replace(lmode[1], "");
5247             }
5248             // strip leading slashes
5249             while(path.substr(0, 1)=="/"){
5250                 path = path.substr(1);
5251             }
5252
5253             while(q && lq != q){
5254                 lq = q;
5255                 var tm = q.match(tagTokenRe);
5256                 if(type == "select"){
5257                     if(tm){
5258                         if(tm[1] == "#"){
5259                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5260                         }else{
5261                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5262                         }
5263                         q = q.replace(tm[0], "");
5264                     }else if(q.substr(0, 1) != '@'){
5265                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5266                     }
5267                 }else{
5268                     if(tm){
5269                         if(tm[1] == "#"){
5270                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5271                         }else{
5272                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5273                         }
5274                         q = q.replace(tm[0], "");
5275                     }
5276                 }
5277                 while(!(mm = q.match(modeRe))){
5278                     var matched = false;
5279                     for(var j = 0; j < tklen; j++){
5280                         var t = tk[j];
5281                         var m = q.match(t.re);
5282                         if(m){
5283                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5284                                                     return m[i];
5285                                                 });
5286                             q = q.replace(m[0], "");
5287                             matched = true;
5288                             break;
5289                         }
5290                     }
5291                     // prevent infinite loop on bad selector
5292                     if(!matched){
5293                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5294                     }
5295                 }
5296                 if(mm[1]){
5297                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5298                     q = q.replace(mm[1], "");
5299                 }
5300             }
5301             fn[fn.length] = "return nodup(n);\n}";
5302             
5303              /** 
5304               * list of variables that need from compression as they are used by eval.
5305              *  eval:var:batch 
5306              *  eval:var:nodup
5307              *  eval:var:byTag
5308              *  eval:var:ById
5309              *  eval:var:getNodes
5310              *  eval:var:quickId
5311              *  eval:var:mode
5312              *  eval:var:root
5313              *  eval:var:n
5314              *  eval:var:byClassName
5315              *  eval:var:byPseudo
5316              *  eval:var:byAttribute
5317              *  eval:var:attrValue
5318              * 
5319              **/ 
5320             eval(fn.join(""));
5321             return f;
5322         },
5323
5324         /**
5325          * Selects a group of elements.
5326          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5327          * @param {Node} root (optional) The start of the query (defaults to document).
5328          * @return {Array}
5329          */
5330         select : function(path, root, type){
5331             if(!root || root == document){
5332                 root = document;
5333             }
5334             if(typeof root == "string"){
5335                 root = document.getElementById(root);
5336             }
5337             var paths = path.split(",");
5338             var results = [];
5339             for(var i = 0, len = paths.length; i < len; i++){
5340                 var p = paths[i].replace(trimRe, "");
5341                 if(!cache[p]){
5342                     cache[p] = Roo.DomQuery.compile(p);
5343                     if(!cache[p]){
5344                         throw p + " is not a valid selector";
5345                     }
5346                 }
5347                 var result = cache[p](root);
5348                 if(result && result != document){
5349                     results = results.concat(result);
5350                 }
5351             }
5352             if(paths.length > 1){
5353                 return nodup(results);
5354             }
5355             return results;
5356         },
5357
5358         /**
5359          * Selects a single element.
5360          * @param {String} selector The selector/xpath query
5361          * @param {Node} root (optional) The start of the query (defaults to document).
5362          * @return {Element}
5363          */
5364         selectNode : function(path, root){
5365             return Roo.DomQuery.select(path, root)[0];
5366         },
5367
5368         /**
5369          * Selects the value of a node, optionally replacing null with the defaultValue.
5370          * @param {String} selector The selector/xpath query
5371          * @param {Node} root (optional) The start of the query (defaults to document).
5372          * @param {String} defaultValue
5373          */
5374         selectValue : function(path, root, defaultValue){
5375             path = path.replace(trimRe, "");
5376             if(!valueCache[path]){
5377                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5378             }
5379             var n = valueCache[path](root);
5380             n = n[0] ? n[0] : n;
5381             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5382             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5383         },
5384
5385         /**
5386          * Selects the value of a node, parsing integers and floats.
5387          * @param {String} selector The selector/xpath query
5388          * @param {Node} root (optional) The start of the query (defaults to document).
5389          * @param {Number} defaultValue
5390          * @return {Number}
5391          */
5392         selectNumber : function(path, root, defaultValue){
5393             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5394             return parseFloat(v);
5395         },
5396
5397         /**
5398          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5399          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5400          * @param {String} selector The simple selector to test
5401          * @return {Boolean}
5402          */
5403         is : function(el, ss){
5404             if(typeof el == "string"){
5405                 el = document.getElementById(el);
5406             }
5407             var isArray = (el instanceof Array);
5408             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5409             return isArray ? (result.length == el.length) : (result.length > 0);
5410         },
5411
5412         /**
5413          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5414          * @param {Array} el An array of elements to filter
5415          * @param {String} selector The simple selector to test
5416          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5417          * the selector instead of the ones that match
5418          * @return {Array}
5419          */
5420         filter : function(els, ss, nonMatches){
5421             ss = ss.replace(trimRe, "");
5422             if(!simpleCache[ss]){
5423                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5424             }
5425             var result = simpleCache[ss](els);
5426             return nonMatches ? quickDiff(result, els) : result;
5427         },
5428
5429         /**
5430          * Collection of matching regular expressions and code snippets.
5431          */
5432         matchers : [{
5433                 re: /^\.([\w-]+)/,
5434                 select: 'n = byClassName(n, null, " {1} ");'
5435             }, {
5436                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5437                 select: 'n = byPseudo(n, "{1}", "{2}");'
5438             },{
5439                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5440                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5441             }, {
5442                 re: /^#([\w-]+)/,
5443                 select: 'n = byId(n, null, "{1}");'
5444             },{
5445                 re: /^@([\w-]+)/,
5446                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5447             }
5448         ],
5449
5450         /**
5451          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5452          * 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;.
5453          */
5454         operators : {
5455             "=" : function(a, v){
5456                 return a == v;
5457             },
5458             "!=" : function(a, v){
5459                 return a != v;
5460             },
5461             "^=" : function(a, v){
5462                 return a && a.substr(0, v.length) == v;
5463             },
5464             "$=" : function(a, v){
5465                 return a && a.substr(a.length-v.length) == v;
5466             },
5467             "*=" : function(a, v){
5468                 return a && a.indexOf(v) !== -1;
5469             },
5470             "%=" : function(a, v){
5471                 return (a % v) == 0;
5472             },
5473             "|=" : function(a, v){
5474                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5475             },
5476             "~=" : function(a, v){
5477                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5478             }
5479         },
5480
5481         /**
5482          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5483          * and the argument (if any) supplied in the selector.
5484          */
5485         pseudos : {
5486             "first-child" : function(c){
5487                 var r = [], ri = -1, n;
5488                 for(var i = 0, ci; ci = n = c[i]; i++){
5489                     while((n = n.previousSibling) && n.nodeType != 1);
5490                     if(!n){
5491                         r[++ri] = ci;
5492                     }
5493                 }
5494                 return r;
5495             },
5496
5497             "last-child" : function(c){
5498                 var r = [], ri = -1, n;
5499                 for(var i = 0, ci; ci = n = c[i]; i++){
5500                     while((n = n.nextSibling) && n.nodeType != 1);
5501                     if(!n){
5502                         r[++ri] = ci;
5503                     }
5504                 }
5505                 return r;
5506             },
5507
5508             "nth-child" : function(c, a) {
5509                 var r = [], ri = -1;
5510                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5511                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5512                 for(var i = 0, n; n = c[i]; i++){
5513                     var pn = n.parentNode;
5514                     if (batch != pn._batch) {
5515                         var j = 0;
5516                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5517                             if(cn.nodeType == 1){
5518                                cn.nodeIndex = ++j;
5519                             }
5520                         }
5521                         pn._batch = batch;
5522                     }
5523                     if (f == 1) {
5524                         if (l == 0 || n.nodeIndex == l){
5525                             r[++ri] = n;
5526                         }
5527                     } else if ((n.nodeIndex + l) % f == 0){
5528                         r[++ri] = n;
5529                     }
5530                 }
5531
5532                 return r;
5533             },
5534
5535             "only-child" : function(c){
5536                 var r = [], ri = -1;;
5537                 for(var i = 0, ci; ci = c[i]; i++){
5538                     if(!prev(ci) && !next(ci)){
5539                         r[++ri] = ci;
5540                     }
5541                 }
5542                 return r;
5543             },
5544
5545             "empty" : function(c){
5546                 var r = [], ri = -1;
5547                 for(var i = 0, ci; ci = c[i]; i++){
5548                     var cns = ci.childNodes, j = 0, cn, empty = true;
5549                     while(cn = cns[j]){
5550                         ++j;
5551                         if(cn.nodeType == 1 || cn.nodeType == 3){
5552                             empty = false;
5553                             break;
5554                         }
5555                     }
5556                     if(empty){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "contains" : function(c, v){
5564                 var r = [], ri = -1;
5565                 for(var i = 0, ci; ci = c[i]; i++){
5566                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5567                         r[++ri] = ci;
5568                     }
5569                 }
5570                 return r;
5571             },
5572
5573             "nodeValue" : function(c, v){
5574                 var r = [], ri = -1;
5575                 for(var i = 0, ci; ci = c[i]; i++){
5576                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5577                         r[++ri] = ci;
5578                     }
5579                 }
5580                 return r;
5581             },
5582
5583             "checked" : function(c){
5584                 var r = [], ri = -1;
5585                 for(var i = 0, ci; ci = c[i]; i++){
5586                     if(ci.checked == true){
5587                         r[++ri] = ci;
5588                     }
5589                 }
5590                 return r;
5591             },
5592
5593             "not" : function(c, ss){
5594                 return Roo.DomQuery.filter(c, ss, true);
5595             },
5596
5597             "odd" : function(c){
5598                 return this["nth-child"](c, "odd");
5599             },
5600
5601             "even" : function(c){
5602                 return this["nth-child"](c, "even");
5603             },
5604
5605             "nth" : function(c, a){
5606                 return c[a-1] || [];
5607             },
5608
5609             "first" : function(c){
5610                 return c[0] || [];
5611             },
5612
5613             "last" : function(c){
5614                 return c[c.length-1] || [];
5615             },
5616
5617             "has" : function(c, ss){
5618                 var s = Roo.DomQuery.select;
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if(s(ss, ci).length > 0){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "next" : function(c, ss){
5629                 var is = Roo.DomQuery.is;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     var n = next(ci);
5633                     if(n && is(n, ss)){
5634                         r[++ri] = ci;
5635                     }
5636                 }
5637                 return r;
5638             },
5639
5640             "prev" : function(c, ss){
5641                 var is = Roo.DomQuery.is;
5642                 var r = [], ri = -1;
5643                 for(var i = 0, ci; ci = c[i]; i++){
5644                     var n = prev(ci);
5645                     if(n && is(n, ss)){
5646                         r[++ri] = ci;
5647                     }
5648                 }
5649                 return r;
5650             }
5651         }
5652     };
5653 }();
5654
5655 /**
5656  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5657  * @param {String} path The selector/xpath query
5658  * @param {Node} root (optional) The start of the query (defaults to document).
5659  * @return {Array}
5660  * @member Roo
5661  * @method query
5662  */
5663 Roo.query = Roo.DomQuery.select;
5664 /*
5665  * Based on:
5666  * Ext JS Library 1.1.1
5667  * Copyright(c) 2006-2007, Ext JS, LLC.
5668  *
5669  * Originally Released Under LGPL - original licence link has changed is not relivant.
5670  *
5671  * Fork - LGPL
5672  * <script type="text/javascript">
5673  */
5674
5675 /**
5676  * @class Roo.util.Observable
5677  * Base class that provides a common interface for publishing events. Subclasses are expected to
5678  * to have a property "events" with all the events defined.<br>
5679  * For example:
5680  * <pre><code>
5681  Employee = function(name){
5682     this.name = name;
5683     this.addEvents({
5684         "fired" : true,
5685         "quit" : true
5686     });
5687  }
5688  Roo.extend(Employee, Roo.util.Observable);
5689 </code></pre>
5690  * @param {Object} config properties to use (incuding events / listeners)
5691  */
5692
5693 Roo.util.Observable = function(cfg){
5694     
5695     cfg = cfg|| {};
5696     this.addEvents(cfg.events || {});
5697     if (cfg.events) {
5698         delete cfg.events; // make sure
5699     }
5700      
5701     Roo.apply(this, cfg);
5702     
5703     if(this.listeners){
5704         this.on(this.listeners);
5705         delete this.listeners;
5706     }
5707 };
5708 Roo.util.Observable.prototype = {
5709     /** 
5710  * @cfg {Object} listeners  list of events and functions to call for this object, 
5711  * For example :
5712  * <pre><code>
5713     listeners :  { 
5714        'click' : function(e) {
5715            ..... 
5716         } ,
5717         .... 
5718     } 
5719   </code></pre>
5720  */
5721     
5722     
5723     /**
5724      * Fires the specified event with the passed parameters (minus the event name).
5725      * @param {String} eventName
5726      * @param {Object...} args Variable number of parameters are passed to handlers
5727      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5728      */
5729     fireEvent : function(){
5730         var ce = this.events[arguments[0].toLowerCase()];
5731         if(typeof ce == "object"){
5732             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5733         }else{
5734             return true;
5735         }
5736     },
5737
5738     // private
5739     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5740
5741     /**
5742      * Appends an event handler to this component
5743      * @param {String}   eventName The type of event to listen for
5744      * @param {Function} handler The method the event invokes
5745      * @param {Object}   scope (optional) The scope in which to execute the handler
5746      * function. The handler function's "this" context.
5747      * @param {Object}   options (optional) An object containing handler configuration
5748      * properties. This may contain any of the following properties:<ul>
5749      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5750      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5751      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5752      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5753      * by the specified number of milliseconds. If the event fires again within that time, the original
5754      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5755      * </ul><br>
5756      * <p>
5757      * <b>Combining Options</b><br>
5758      * Using the options argument, it is possible to combine different types of listeners:<br>
5759      * <br>
5760      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5761                 <pre><code>
5762                 el.on('click', this.onClick, this, {
5763                         single: true,
5764                 delay: 100,
5765                 forumId: 4
5766                 });
5767                 </code></pre>
5768      * <p>
5769      * <b>Attaching multiple handlers in 1 call</b><br>
5770      * The method also allows for a single argument to be passed which is a config object containing properties
5771      * which specify multiple handlers.
5772      * <pre><code>
5773                 el.on({
5774                         'click': {
5775                         fn: this.onClick,
5776                         scope: this,
5777                         delay: 100
5778                 }, 
5779                 'mouseover': {
5780                         fn: this.onMouseOver,
5781                         scope: this
5782                 },
5783                 'mouseout': {
5784                         fn: this.onMouseOut,
5785                         scope: this
5786                 }
5787                 });
5788                 </code></pre>
5789      * <p>
5790      * Or a shorthand syntax which passes the same scope object to all handlers:
5791         <pre><code>
5792                 el.on({
5793                         'click': this.onClick,
5794                 'mouseover': this.onMouseOver,
5795                 'mouseout': this.onMouseOut,
5796                 scope: this
5797                 });
5798                 </code></pre>
5799      */
5800     addListener : function(eventName, fn, scope, o){
5801         if(typeof eventName == "object"){
5802             o = eventName;
5803             for(var e in o){
5804                 if(this.filterOptRe.test(e)){
5805                     continue;
5806                 }
5807                 if(typeof o[e] == "function"){
5808                     // shared options
5809                     this.addListener(e, o[e], o.scope,  o);
5810                 }else{
5811                     // individual options
5812                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5813                 }
5814             }
5815             return;
5816         }
5817         o = (!o || typeof o == "boolean") ? {} : o;
5818         eventName = eventName.toLowerCase();
5819         var ce = this.events[eventName] || true;
5820         if(typeof ce == "boolean"){
5821             ce = new Roo.util.Event(this, eventName);
5822             this.events[eventName] = ce;
5823         }
5824         ce.addListener(fn, scope, o);
5825     },
5826
5827     /**
5828      * Removes a listener
5829      * @param {String}   eventName     The type of event to listen for
5830      * @param {Function} handler        The handler to remove
5831      * @param {Object}   scope  (optional) The scope (this object) for the handler
5832      */
5833     removeListener : function(eventName, fn, scope){
5834         var ce = this.events[eventName.toLowerCase()];
5835         if(typeof ce == "object"){
5836             ce.removeListener(fn, scope);
5837         }
5838     },
5839
5840     /**
5841      * Removes all listeners for this object
5842      */
5843     purgeListeners : function(){
5844         for(var evt in this.events){
5845             if(typeof this.events[evt] == "object"){
5846                  this.events[evt].clearListeners();
5847             }
5848         }
5849     },
5850
5851     relayEvents : function(o, events){
5852         var createHandler = function(ename){
5853             return function(){
5854                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5855             };
5856         };
5857         for(var i = 0, len = events.length; i < len; i++){
5858             var ename = events[i];
5859             if(!this.events[ename]){ this.events[ename] = true; };
5860             o.on(ename, createHandler(ename), this);
5861         }
5862     },
5863
5864     /**
5865      * Used to define events on this Observable
5866      * @param {Object} object The object with the events defined
5867      */
5868     addEvents : function(o){
5869         if(!this.events){
5870             this.events = {};
5871         }
5872         Roo.applyIf(this.events, o);
5873     },
5874
5875     /**
5876      * Checks to see if this object has any listeners for a specified event
5877      * @param {String} eventName The name of the event to check for
5878      * @return {Boolean} True if the event is being listened for, else false
5879      */
5880     hasListener : function(eventName){
5881         var e = this.events[eventName];
5882         return typeof e == "object" && e.listeners.length > 0;
5883     }
5884 };
5885 /**
5886  * Appends an event handler to this element (shorthand for addListener)
5887  * @param {String}   eventName     The type of event to listen for
5888  * @param {Function} handler        The method the event invokes
5889  * @param {Object}   scope (optional) The scope in which to execute the handler
5890  * function. The handler function's "this" context.
5891  * @param {Object}   options  (optional)
5892  * @method
5893  */
5894 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5895 /**
5896  * Removes a listener (shorthand for removeListener)
5897  * @param {String}   eventName     The type of event to listen for
5898  * @param {Function} handler        The handler to remove
5899  * @param {Object}   scope  (optional) The scope (this object) for the handler
5900  * @method
5901  */
5902 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5903
5904 /**
5905  * Starts capture on the specified Observable. All events will be passed
5906  * to the supplied function with the event name + standard signature of the event
5907  * <b>before</b> the event is fired. If the supplied function returns false,
5908  * the event will not fire.
5909  * @param {Observable} o The Observable to capture
5910  * @param {Function} fn The function to call
5911  * @param {Object} scope (optional) The scope (this object) for the fn
5912  * @static
5913  */
5914 Roo.util.Observable.capture = function(o, fn, scope){
5915     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5916 };
5917
5918 /**
5919  * Removes <b>all</b> added captures from the Observable.
5920  * @param {Observable} o The Observable to release
5921  * @static
5922  */
5923 Roo.util.Observable.releaseCapture = function(o){
5924     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5925 };
5926
5927 (function(){
5928
5929     var createBuffered = function(h, o, scope){
5930         var task = new Roo.util.DelayedTask();
5931         return function(){
5932             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5933         };
5934     };
5935
5936     var createSingle = function(h, e, fn, scope){
5937         return function(){
5938             e.removeListener(fn, scope);
5939             return h.apply(scope, arguments);
5940         };
5941     };
5942
5943     var createDelayed = function(h, o, scope){
5944         return function(){
5945             var args = Array.prototype.slice.call(arguments, 0);
5946             setTimeout(function(){
5947                 h.apply(scope, args);
5948             }, o.delay || 10);
5949         };
5950     };
5951
5952     Roo.util.Event = function(obj, name){
5953         this.name = name;
5954         this.obj = obj;
5955         this.listeners = [];
5956     };
5957
5958     Roo.util.Event.prototype = {
5959         addListener : function(fn, scope, options){
5960             var o = options || {};
5961             scope = scope || this.obj;
5962             if(!this.isListening(fn, scope)){
5963                 var l = {fn: fn, scope: scope, options: o};
5964                 var h = fn;
5965                 if(o.delay){
5966                     h = createDelayed(h, o, scope);
5967                 }
5968                 if(o.single){
5969                     h = createSingle(h, this, fn, scope);
5970                 }
5971                 if(o.buffer){
5972                     h = createBuffered(h, o, scope);
5973                 }
5974                 l.fireFn = h;
5975                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5976                     this.listeners.push(l);
5977                 }else{
5978                     this.listeners = this.listeners.slice(0);
5979                     this.listeners.push(l);
5980                 }
5981             }
5982         },
5983
5984         findListener : function(fn, scope){
5985             scope = scope || this.obj;
5986             var ls = this.listeners;
5987             for(var i = 0, len = ls.length; i < len; i++){
5988                 var l = ls[i];
5989                 if(l.fn == fn && l.scope == scope){
5990                     return i;
5991                 }
5992             }
5993             return -1;
5994         },
5995
5996         isListening : function(fn, scope){
5997             return this.findListener(fn, scope) != -1;
5998         },
5999
6000         removeListener : function(fn, scope){
6001             var index;
6002             if((index = this.findListener(fn, scope)) != -1){
6003                 if(!this.firing){
6004                     this.listeners.splice(index, 1);
6005                 }else{
6006                     this.listeners = this.listeners.slice(0);
6007                     this.listeners.splice(index, 1);
6008                 }
6009                 return true;
6010             }
6011             return false;
6012         },
6013
6014         clearListeners : function(){
6015             this.listeners = [];
6016         },
6017
6018         fire : function(){
6019             var ls = this.listeners, scope, len = ls.length;
6020             if(len > 0){
6021                 this.firing = true;
6022                 var args = Array.prototype.slice.call(arguments, 0);
6023                 for(var i = 0; i < len; i++){
6024                     var l = ls[i];
6025                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6026                         this.firing = false;
6027                         return false;
6028                     }
6029                 }
6030                 this.firing = false;
6031             }
6032             return true;
6033         }
6034     };
6035 })();/*
6036  * Based on:
6037  * Ext JS Library 1.1.1
6038  * Copyright(c) 2006-2007, Ext JS, LLC.
6039  *
6040  * Originally Released Under LGPL - original licence link has changed is not relivant.
6041  *
6042  * Fork - LGPL
6043  * <script type="text/javascript">
6044  */
6045
6046 /**
6047  * @class Roo.EventManager
6048  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6049  * several useful events directly.
6050  * See {@link Roo.EventObject} for more details on normalized event objects.
6051  * @singleton
6052  */
6053 Roo.EventManager = function(){
6054     var docReadyEvent, docReadyProcId, docReadyState = false;
6055     var resizeEvent, resizeTask, textEvent, textSize;
6056     var E = Roo.lib.Event;
6057     var D = Roo.lib.Dom;
6058
6059     
6060     
6061
6062     var fireDocReady = function(){
6063         if(!docReadyState){
6064             docReadyState = true;
6065             Roo.isReady = true;
6066             if(docReadyProcId){
6067                 clearInterval(docReadyProcId);
6068             }
6069             if(Roo.isGecko || Roo.isOpera) {
6070                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6071             }
6072             if(Roo.isIE){
6073                 var defer = document.getElementById("ie-deferred-loader");
6074                 if(defer){
6075                     defer.onreadystatechange = null;
6076                     defer.parentNode.removeChild(defer);
6077                 }
6078             }
6079             if(docReadyEvent){
6080                 docReadyEvent.fire();
6081                 docReadyEvent.clearListeners();
6082             }
6083         }
6084     };
6085     
6086     var initDocReady = function(){
6087         docReadyEvent = new Roo.util.Event();
6088         if(Roo.isGecko || Roo.isOpera) {
6089             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6090         }else if(Roo.isIE){
6091             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6092             var defer = document.getElementById("ie-deferred-loader");
6093             defer.onreadystatechange = function(){
6094                 if(this.readyState == "complete"){
6095                     fireDocReady();
6096                 }
6097             };
6098         }else if(Roo.isSafari){ 
6099             docReadyProcId = setInterval(function(){
6100                 var rs = document.readyState;
6101                 if(rs == "complete") {
6102                     fireDocReady();     
6103                  }
6104             }, 10);
6105         }
6106         // no matter what, make sure it fires on load
6107         E.on(window, "load", fireDocReady);
6108     };
6109
6110     var createBuffered = function(h, o){
6111         var task = new Roo.util.DelayedTask(h);
6112         return function(e){
6113             // create new event object impl so new events don't wipe out properties
6114             e = new Roo.EventObjectImpl(e);
6115             task.delay(o.buffer, h, null, [e]);
6116         };
6117     };
6118
6119     var createSingle = function(h, el, ename, fn){
6120         return function(e){
6121             Roo.EventManager.removeListener(el, ename, fn);
6122             h(e);
6123         };
6124     };
6125
6126     var createDelayed = function(h, o){
6127         return function(e){
6128             // create new event object impl so new events don't wipe out properties
6129             e = new Roo.EventObjectImpl(e);
6130             setTimeout(function(){
6131                 h(e);
6132             }, o.delay || 10);
6133         };
6134     };
6135     var transitionEndVal = false;
6136     
6137     var transitionEnd = function()
6138     {
6139         if (transitionEndVal) {
6140             return transitionEndVal;
6141         }
6142         var el = document.createElement('div');
6143
6144         var transEndEventNames = {
6145             WebkitTransition : 'webkitTransitionEnd',
6146             MozTransition    : 'transitionend',
6147             OTransition      : 'oTransitionEnd otransitionend',
6148             transition       : 'transitionend'
6149         };
6150     
6151         for (var name in transEndEventNames) {
6152             if (el.style[name] !== undefined) {
6153                 transitionEndVal = transEndEventNames[name];
6154                 return  transitionEndVal ;
6155             }
6156         }
6157     }
6158     
6159
6160     var listen = function(element, ename, opt, fn, scope){
6161         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6162         fn = fn || o.fn; scope = scope || o.scope;
6163         var el = Roo.getDom(element);
6164         
6165         
6166         if(!el){
6167             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6168         }
6169         
6170         if (ename == 'transitionend') {
6171             ename = transitionEnd();
6172         }
6173         var h = function(e){
6174             e = Roo.EventObject.setEvent(e);
6175             var t;
6176             if(o.delegate){
6177                 t = e.getTarget(o.delegate, el);
6178                 if(!t){
6179                     return;
6180                 }
6181             }else{
6182                 t = e.target;
6183             }
6184             if(o.stopEvent === true){
6185                 e.stopEvent();
6186             }
6187             if(o.preventDefault === true){
6188                e.preventDefault();
6189             }
6190             if(o.stopPropagation === true){
6191                 e.stopPropagation();
6192             }
6193
6194             if(o.normalized === false){
6195                 e = e.browserEvent;
6196             }
6197
6198             fn.call(scope || el, e, t, o);
6199         };
6200         if(o.delay){
6201             h = createDelayed(h, o);
6202         }
6203         if(o.single){
6204             h = createSingle(h, el, ename, fn);
6205         }
6206         if(o.buffer){
6207             h = createBuffered(h, o);
6208         }
6209         fn._handlers = fn._handlers || [];
6210         
6211         
6212         fn._handlers.push([Roo.id(el), ename, h]);
6213         
6214         
6215          
6216         E.on(el, ename, h);
6217         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6218             el.addEventListener("DOMMouseScroll", h, false);
6219             E.on(window, 'unload', function(){
6220                 el.removeEventListener("DOMMouseScroll", h, false);
6221             });
6222         }
6223         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6224             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6225         }
6226         return h;
6227     };
6228
6229     var stopListening = function(el, ename, fn){
6230         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6231         if(hds){
6232             for(var i = 0, len = hds.length; i < len; i++){
6233                 var h = hds[i];
6234                 if(h[0] == id && h[1] == ename){
6235                     hd = h[2];
6236                     hds.splice(i, 1);
6237                     break;
6238                 }
6239             }
6240         }
6241         E.un(el, ename, hd);
6242         el = Roo.getDom(el);
6243         if(ename == "mousewheel" && el.addEventListener){
6244             el.removeEventListener("DOMMouseScroll", hd, false);
6245         }
6246         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6247             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6248         }
6249     };
6250
6251     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6252     
6253     var pub = {
6254         
6255         
6256         /** 
6257          * Fix for doc tools
6258          * @scope Roo.EventManager
6259          */
6260         
6261         
6262         /** 
6263          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6264          * object with a Roo.EventObject
6265          * @param {Function} fn        The method the event invokes
6266          * @param {Object}   scope    An object that becomes the scope of the handler
6267          * @param {boolean}  override If true, the obj passed in becomes
6268          *                             the execution scope of the listener
6269          * @return {Function} The wrapped function
6270          * @deprecated
6271          */
6272         wrap : function(fn, scope, override){
6273             return function(e){
6274                 Roo.EventObject.setEvent(e);
6275                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6276             };
6277         },
6278         
6279         /**
6280      * Appends an event handler to an element (shorthand for addListener)
6281      * @param {String/HTMLElement}   element        The html element or id to assign the
6282      * @param {String}   eventName The type of event to listen for
6283      * @param {Function} handler The method the event invokes
6284      * @param {Object}   scope (optional) The scope in which to execute the handler
6285      * function. The handler function's "this" context.
6286      * @param {Object}   options (optional) An object containing handler configuration
6287      * properties. This may contain any of the following properties:<ul>
6288      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6289      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6290      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6291      * <li>preventDefault {Boolean} True to prevent the default action</li>
6292      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6293      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6294      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6295      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6296      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6297      * by the specified number of milliseconds. If the event fires again within that time, the original
6298      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6299      * </ul><br>
6300      * <p>
6301      * <b>Combining Options</b><br>
6302      * Using the options argument, it is possible to combine different types of listeners:<br>
6303      * <br>
6304      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6305      * Code:<pre><code>
6306 el.on('click', this.onClick, this, {
6307     single: true,
6308     delay: 100,
6309     stopEvent : true,
6310     forumId: 4
6311 });</code></pre>
6312      * <p>
6313      * <b>Attaching multiple handlers in 1 call</b><br>
6314       * The method also allows for a single argument to be passed which is a config object containing properties
6315      * which specify multiple handlers.
6316      * <p>
6317      * Code:<pre><code>
6318 el.on({
6319     'click' : {
6320         fn: this.onClick
6321         scope: this,
6322         delay: 100
6323     },
6324     'mouseover' : {
6325         fn: this.onMouseOver
6326         scope: this
6327     },
6328     'mouseout' : {
6329         fn: this.onMouseOut
6330         scope: this
6331     }
6332 });</code></pre>
6333      * <p>
6334      * Or a shorthand syntax:<br>
6335      * Code:<pre><code>
6336 el.on({
6337     'click' : this.onClick,
6338     'mouseover' : this.onMouseOver,
6339     'mouseout' : this.onMouseOut
6340     scope: this
6341 });</code></pre>
6342      */
6343         addListener : function(element, eventName, fn, scope, options){
6344             if(typeof eventName == "object"){
6345                 var o = eventName;
6346                 for(var e in o){
6347                     if(propRe.test(e)){
6348                         continue;
6349                     }
6350                     if(typeof o[e] == "function"){
6351                         // shared options
6352                         listen(element, e, o, o[e], o.scope);
6353                     }else{
6354                         // individual options
6355                         listen(element, e, o[e]);
6356                     }
6357                 }
6358                 return;
6359             }
6360             return listen(element, eventName, options, fn, scope);
6361         },
6362         
6363         /**
6364          * Removes an event handler
6365          *
6366          * @param {String/HTMLElement}   element        The id or html element to remove the 
6367          *                             event from
6368          * @param {String}   eventName     The type of event
6369          * @param {Function} fn
6370          * @return {Boolean} True if a listener was actually removed
6371          */
6372         removeListener : function(element, eventName, fn){
6373             return stopListening(element, eventName, fn);
6374         },
6375         
6376         /**
6377          * Fires when the document is ready (before onload and before images are loaded). Can be 
6378          * accessed shorthanded Roo.onReady().
6379          * @param {Function} fn        The method the event invokes
6380          * @param {Object}   scope    An  object that becomes the scope of the handler
6381          * @param {boolean}  options
6382          */
6383         onDocumentReady : function(fn, scope, options){
6384             if(docReadyState){ // if it already fired
6385                 docReadyEvent.addListener(fn, scope, options);
6386                 docReadyEvent.fire();
6387                 docReadyEvent.clearListeners();
6388                 return;
6389             }
6390             if(!docReadyEvent){
6391                 initDocReady();
6392             }
6393             docReadyEvent.addListener(fn, scope, options);
6394         },
6395         
6396         /**
6397          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6398          * @param {Function} fn        The method the event invokes
6399          * @param {Object}   scope    An object that becomes the scope of the handler
6400          * @param {boolean}  options
6401          */
6402         onWindowResize : function(fn, scope, options){
6403             if(!resizeEvent){
6404                 resizeEvent = new Roo.util.Event();
6405                 resizeTask = new Roo.util.DelayedTask(function(){
6406                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6407                 });
6408                 E.on(window, "resize", function(){
6409                     if(Roo.isIE){
6410                         resizeTask.delay(50);
6411                     }else{
6412                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6413                     }
6414                 });
6415             }
6416             resizeEvent.addListener(fn, scope, options);
6417         },
6418
6419         /**
6420          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6421          * @param {Function} fn        The method the event invokes
6422          * @param {Object}   scope    An object that becomes the scope of the handler
6423          * @param {boolean}  options
6424          */
6425         onTextResize : function(fn, scope, options){
6426             if(!textEvent){
6427                 textEvent = new Roo.util.Event();
6428                 var textEl = new Roo.Element(document.createElement('div'));
6429                 textEl.dom.className = 'x-text-resize';
6430                 textEl.dom.innerHTML = 'X';
6431                 textEl.appendTo(document.body);
6432                 textSize = textEl.dom.offsetHeight;
6433                 setInterval(function(){
6434                     if(textEl.dom.offsetHeight != textSize){
6435                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6436                     }
6437                 }, this.textResizeInterval);
6438             }
6439             textEvent.addListener(fn, scope, options);
6440         },
6441
6442         /**
6443          * Removes the passed window resize listener.
6444          * @param {Function} fn        The method the event invokes
6445          * @param {Object}   scope    The scope of handler
6446          */
6447         removeResizeListener : function(fn, scope){
6448             if(resizeEvent){
6449                 resizeEvent.removeListener(fn, scope);
6450             }
6451         },
6452
6453         // private
6454         fireResize : function(){
6455             if(resizeEvent){
6456                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6457             }   
6458         },
6459         /**
6460          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6461          */
6462         ieDeferSrc : false,
6463         /**
6464          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6465          */
6466         textResizeInterval : 50
6467     };
6468     
6469     /**
6470      * Fix for doc tools
6471      * @scopeAlias pub=Roo.EventManager
6472      */
6473     
6474      /**
6475      * Appends an event handler to an element (shorthand for addListener)
6476      * @param {String/HTMLElement}   element        The html element or id to assign the
6477      * @param {String}   eventName The type of event to listen for
6478      * @param {Function} handler The method the event invokes
6479      * @param {Object}   scope (optional) The scope in which to execute the handler
6480      * function. The handler function's "this" context.
6481      * @param {Object}   options (optional) An object containing handler configuration
6482      * properties. This may contain any of the following properties:<ul>
6483      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6484      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6485      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6486      * <li>preventDefault {Boolean} True to prevent the default action</li>
6487      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6488      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6489      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6490      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6491      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6492      * by the specified number of milliseconds. If the event fires again within that time, the original
6493      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6494      * </ul><br>
6495      * <p>
6496      * <b>Combining Options</b><br>
6497      * Using the options argument, it is possible to combine different types of listeners:<br>
6498      * <br>
6499      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6500      * Code:<pre><code>
6501 el.on('click', this.onClick, this, {
6502     single: true,
6503     delay: 100,
6504     stopEvent : true,
6505     forumId: 4
6506 });</code></pre>
6507      * <p>
6508      * <b>Attaching multiple handlers in 1 call</b><br>
6509       * The method also allows for a single argument to be passed which is a config object containing properties
6510      * which specify multiple handlers.
6511      * <p>
6512      * Code:<pre><code>
6513 el.on({
6514     'click' : {
6515         fn: this.onClick
6516         scope: this,
6517         delay: 100
6518     },
6519     'mouseover' : {
6520         fn: this.onMouseOver
6521         scope: this
6522     },
6523     'mouseout' : {
6524         fn: this.onMouseOut
6525         scope: this
6526     }
6527 });</code></pre>
6528      * <p>
6529      * Or a shorthand syntax:<br>
6530      * Code:<pre><code>
6531 el.on({
6532     'click' : this.onClick,
6533     'mouseover' : this.onMouseOver,
6534     'mouseout' : this.onMouseOut
6535     scope: this
6536 });</code></pre>
6537      */
6538     pub.on = pub.addListener;
6539     pub.un = pub.removeListener;
6540
6541     pub.stoppedMouseDownEvent = new Roo.util.Event();
6542     return pub;
6543 }();
6544 /**
6545   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6546   * @param {Function} fn        The method the event invokes
6547   * @param {Object}   scope    An  object that becomes the scope of the handler
6548   * @param {boolean}  override If true, the obj passed in becomes
6549   *                             the execution scope of the listener
6550   * @member Roo
6551   * @method onReady
6552  */
6553 Roo.onReady = Roo.EventManager.onDocumentReady;
6554
6555 Roo.onReady(function(){
6556     var bd = Roo.get(document.body);
6557     if(!bd){ return; }
6558
6559     var cls = [
6560             Roo.isIE ? "roo-ie"
6561             : Roo.isGecko ? "roo-gecko"
6562             : Roo.isOpera ? "roo-opera"
6563             : Roo.isSafari ? "roo-safari" : ""];
6564
6565     if(Roo.isMac){
6566         cls.push("roo-mac");
6567     }
6568     if(Roo.isLinux){
6569         cls.push("roo-linux");
6570     }
6571     if(Roo.isBorderBox){
6572         cls.push('roo-border-box');
6573     }
6574     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6575         var p = bd.dom.parentNode;
6576         if(p){
6577             p.className += ' roo-strict';
6578         }
6579     }
6580     bd.addClass(cls.join(' '));
6581 });
6582
6583 /**
6584  * @class Roo.EventObject
6585  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6586  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6587  * Example:
6588  * <pre><code>
6589  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6590     e.preventDefault();
6591     var target = e.getTarget();
6592     ...
6593  }
6594  var myDiv = Roo.get("myDiv");
6595  myDiv.on("click", handleClick);
6596  //or
6597  Roo.EventManager.on("myDiv", 'click', handleClick);
6598  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6599  </code></pre>
6600  * @singleton
6601  */
6602 Roo.EventObject = function(){
6603     
6604     var E = Roo.lib.Event;
6605     
6606     // safari keypress events for special keys return bad keycodes
6607     var safariKeys = {
6608         63234 : 37, // left
6609         63235 : 39, // right
6610         63232 : 38, // up
6611         63233 : 40, // down
6612         63276 : 33, // page up
6613         63277 : 34, // page down
6614         63272 : 46, // delete
6615         63273 : 36, // home
6616         63275 : 35  // end
6617     };
6618
6619     // normalize button clicks
6620     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6621                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6622
6623     Roo.EventObjectImpl = function(e){
6624         if(e){
6625             this.setEvent(e.browserEvent || e);
6626         }
6627     };
6628     Roo.EventObjectImpl.prototype = {
6629         /**
6630          * Used to fix doc tools.
6631          * @scope Roo.EventObject.prototype
6632          */
6633             
6634
6635         
6636         
6637         /** The normal browser event */
6638         browserEvent : null,
6639         /** The button pressed in a mouse event */
6640         button : -1,
6641         /** True if the shift key was down during the event */
6642         shiftKey : false,
6643         /** True if the control key was down during the event */
6644         ctrlKey : false,
6645         /** True if the alt key was down during the event */
6646         altKey : false,
6647
6648         /** Key constant 
6649         * @type Number */
6650         BACKSPACE : 8,
6651         /** Key constant 
6652         * @type Number */
6653         TAB : 9,
6654         /** Key constant 
6655         * @type Number */
6656         RETURN : 13,
6657         /** Key constant 
6658         * @type Number */
6659         ENTER : 13,
6660         /** Key constant 
6661         * @type Number */
6662         SHIFT : 16,
6663         /** Key constant 
6664         * @type Number */
6665         CONTROL : 17,
6666         /** Key constant 
6667         * @type Number */
6668         ESC : 27,
6669         /** Key constant 
6670         * @type Number */
6671         SPACE : 32,
6672         /** Key constant 
6673         * @type Number */
6674         PAGEUP : 33,
6675         /** Key constant 
6676         * @type Number */
6677         PAGEDOWN : 34,
6678         /** Key constant 
6679         * @type Number */
6680         END : 35,
6681         /** Key constant 
6682         * @type Number */
6683         HOME : 36,
6684         /** Key constant 
6685         * @type Number */
6686         LEFT : 37,
6687         /** Key constant 
6688         * @type Number */
6689         UP : 38,
6690         /** Key constant 
6691         * @type Number */
6692         RIGHT : 39,
6693         /** Key constant 
6694         * @type Number */
6695         DOWN : 40,
6696         /** Key constant 
6697         * @type Number */
6698         DELETE : 46,
6699         /** Key constant 
6700         * @type Number */
6701         F5 : 116,
6702
6703            /** @private */
6704         setEvent : function(e){
6705             if(e == this || (e && e.browserEvent)){ // already wrapped
6706                 return e;
6707             }
6708             this.browserEvent = e;
6709             if(e){
6710                 // normalize buttons
6711                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6712                 if(e.type == 'click' && this.button == -1){
6713                     this.button = 0;
6714                 }
6715                 this.type = e.type;
6716                 this.shiftKey = e.shiftKey;
6717                 // mac metaKey behaves like ctrlKey
6718                 this.ctrlKey = e.ctrlKey || e.metaKey;
6719                 this.altKey = e.altKey;
6720                 // in getKey these will be normalized for the mac
6721                 this.keyCode = e.keyCode;
6722                 // keyup warnings on firefox.
6723                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6724                 // cache the target for the delayed and or buffered events
6725                 this.target = E.getTarget(e);
6726                 // same for XY
6727                 this.xy = E.getXY(e);
6728             }else{
6729                 this.button = -1;
6730                 this.shiftKey = false;
6731                 this.ctrlKey = false;
6732                 this.altKey = false;
6733                 this.keyCode = 0;
6734                 this.charCode =0;
6735                 this.target = null;
6736                 this.xy = [0, 0];
6737             }
6738             return this;
6739         },
6740
6741         /**
6742          * Stop the event (preventDefault and stopPropagation)
6743          */
6744         stopEvent : function(){
6745             if(this.browserEvent){
6746                 if(this.browserEvent.type == 'mousedown'){
6747                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6748                 }
6749                 E.stopEvent(this.browserEvent);
6750             }
6751         },
6752
6753         /**
6754          * Prevents the browsers default handling of the event.
6755          */
6756         preventDefault : function(){
6757             if(this.browserEvent){
6758                 E.preventDefault(this.browserEvent);
6759             }
6760         },
6761
6762         /** @private */
6763         isNavKeyPress : function(){
6764             var k = this.keyCode;
6765             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6766             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6767         },
6768
6769         isSpecialKey : function(){
6770             var k = this.keyCode;
6771             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6772             (k == 16) || (k == 17) ||
6773             (k >= 18 && k <= 20) ||
6774             (k >= 33 && k <= 35) ||
6775             (k >= 36 && k <= 39) ||
6776             (k >= 44 && k <= 45);
6777         },
6778         /**
6779          * Cancels bubbling of the event.
6780          */
6781         stopPropagation : function(){
6782             if(this.browserEvent){
6783                 if(this.type == 'mousedown'){
6784                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6785                 }
6786                 E.stopPropagation(this.browserEvent);
6787             }
6788         },
6789
6790         /**
6791          * Gets the key code for the event.
6792          * @return {Number}
6793          */
6794         getCharCode : function(){
6795             return this.charCode || this.keyCode;
6796         },
6797
6798         /**
6799          * Returns a normalized keyCode for the event.
6800          * @return {Number} The key code
6801          */
6802         getKey : function(){
6803             var k = this.keyCode || this.charCode;
6804             return Roo.isSafari ? (safariKeys[k] || k) : k;
6805         },
6806
6807         /**
6808          * Gets the x coordinate of the event.
6809          * @return {Number}
6810          */
6811         getPageX : function(){
6812             return this.xy[0];
6813         },
6814
6815         /**
6816          * Gets the y coordinate of the event.
6817          * @return {Number}
6818          */
6819         getPageY : function(){
6820             return this.xy[1];
6821         },
6822
6823         /**
6824          * Gets the time of the event.
6825          * @return {Number}
6826          */
6827         getTime : function(){
6828             if(this.browserEvent){
6829                 return E.getTime(this.browserEvent);
6830             }
6831             return null;
6832         },
6833
6834         /**
6835          * Gets the page coordinates of the event.
6836          * @return {Array} The xy values like [x, y]
6837          */
6838         getXY : function(){
6839             return this.xy;
6840         },
6841
6842         /**
6843          * Gets the target for the event.
6844          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6845          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6846                 search as a number or element (defaults to 10 || document.body)
6847          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6848          * @return {HTMLelement}
6849          */
6850         getTarget : function(selector, maxDepth, returnEl){
6851             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6852         },
6853         /**
6854          * Gets the related target.
6855          * @return {HTMLElement}
6856          */
6857         getRelatedTarget : function(){
6858             if(this.browserEvent){
6859                 return E.getRelatedTarget(this.browserEvent);
6860             }
6861             return null;
6862         },
6863
6864         /**
6865          * Normalizes mouse wheel delta across browsers
6866          * @return {Number} The delta
6867          */
6868         getWheelDelta : function(){
6869             var e = this.browserEvent;
6870             var delta = 0;
6871             if(e.wheelDelta){ /* IE/Opera. */
6872                 delta = e.wheelDelta/120;
6873             }else if(e.detail){ /* Mozilla case. */
6874                 delta = -e.detail/3;
6875             }
6876             return delta;
6877         },
6878
6879         /**
6880          * Returns true if the control, meta, shift or alt key was pressed during this event.
6881          * @return {Boolean}
6882          */
6883         hasModifier : function(){
6884             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6885         },
6886
6887         /**
6888          * Returns true if the target of this event equals el or is a child of el
6889          * @param {String/HTMLElement/Element} el
6890          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6891          * @return {Boolean}
6892          */
6893         within : function(el, related){
6894             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6895             return t && Roo.fly(el).contains(t);
6896         },
6897
6898         getPoint : function(){
6899             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6900         }
6901     };
6902
6903     return new Roo.EventObjectImpl();
6904 }();
6905             
6906     /*
6907  * Based on:
6908  * Ext JS Library 1.1.1
6909  * Copyright(c) 2006-2007, Ext JS, LLC.
6910  *
6911  * Originally Released Under LGPL - original licence link has changed is not relivant.
6912  *
6913  * Fork - LGPL
6914  * <script type="text/javascript">
6915  */
6916
6917  
6918 // was in Composite Element!??!?!
6919  
6920 (function(){
6921     var D = Roo.lib.Dom;
6922     var E = Roo.lib.Event;
6923     var A = Roo.lib.Anim;
6924
6925     // local style camelizing for speed
6926     var propCache = {};
6927     var camelRe = /(-[a-z])/gi;
6928     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6929     var view = document.defaultView;
6930
6931 /**
6932  * @class Roo.Element
6933  * Represents an Element in the DOM.<br><br>
6934  * Usage:<br>
6935 <pre><code>
6936 var el = Roo.get("my-div");
6937
6938 // or with getEl
6939 var el = getEl("my-div");
6940
6941 // or with a DOM element
6942 var el = Roo.get(myDivElement);
6943 </code></pre>
6944  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6945  * each call instead of constructing a new one.<br><br>
6946  * <b>Animations</b><br />
6947  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6948  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6949 <pre>
6950 Option    Default   Description
6951 --------- --------  ---------------------------------------------
6952 duration  .35       The duration of the animation in seconds
6953 easing    easeOut   The YUI easing method
6954 callback  none      A function to execute when the anim completes
6955 scope     this      The scope (this) of the callback function
6956 </pre>
6957 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6958 * manipulate the animation. Here's an example:
6959 <pre><code>
6960 var el = Roo.get("my-div");
6961
6962 // no animation
6963 el.setWidth(100);
6964
6965 // default animation
6966 el.setWidth(100, true);
6967
6968 // animation with some options set
6969 el.setWidth(100, {
6970     duration: 1,
6971     callback: this.foo,
6972     scope: this
6973 });
6974
6975 // using the "anim" property to get the Anim object
6976 var opt = {
6977     duration: 1,
6978     callback: this.foo,
6979     scope: this
6980 };
6981 el.setWidth(100, opt);
6982 ...
6983 if(opt.anim.isAnimated()){
6984     opt.anim.stop();
6985 }
6986 </code></pre>
6987 * <b> Composite (Collections of) Elements</b><br />
6988  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6989  * @constructor Create a new Element directly.
6990  * @param {String/HTMLElement} element
6991  * @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).
6992  */
6993     Roo.Element = function(element, forceNew){
6994         var dom = typeof element == "string" ?
6995                 document.getElementById(element) : element;
6996         if(!dom){ // invalid id/element
6997             return null;
6998         }
6999         var id = dom.id;
7000         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7001             return Roo.Element.cache[id];
7002         }
7003
7004         /**
7005          * The DOM element
7006          * @type HTMLElement
7007          */
7008         this.dom = dom;
7009
7010         /**
7011          * The DOM element ID
7012          * @type String
7013          */
7014         this.id = id || Roo.id(dom);
7015     };
7016
7017     var El = Roo.Element;
7018
7019     El.prototype = {
7020         /**
7021          * The element's default display mode  (defaults to "")
7022          * @type String
7023          */
7024         originalDisplay : "",
7025
7026         visibilityMode : 1,
7027         /**
7028          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7029          * @type String
7030          */
7031         defaultUnit : "px",
7032         /**
7033          * Sets the element's visibility mode. When setVisible() is called it
7034          * will use this to determine whether to set the visibility or the display property.
7035          * @param visMode Element.VISIBILITY or Element.DISPLAY
7036          * @return {Roo.Element} this
7037          */
7038         setVisibilityMode : function(visMode){
7039             this.visibilityMode = visMode;
7040             return this;
7041         },
7042         /**
7043          * Convenience method for setVisibilityMode(Element.DISPLAY)
7044          * @param {String} display (optional) What to set display to when visible
7045          * @return {Roo.Element} this
7046          */
7047         enableDisplayMode : function(display){
7048             this.setVisibilityMode(El.DISPLAY);
7049             if(typeof display != "undefined") this.originalDisplay = display;
7050             return this;
7051         },
7052
7053         /**
7054          * 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)
7055          * @param {String} selector The simple selector to test
7056          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7057                 search as a number or element (defaults to 10 || document.body)
7058          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7059          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7060          */
7061         findParent : function(simpleSelector, maxDepth, returnEl){
7062             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7063             maxDepth = maxDepth || 50;
7064             if(typeof maxDepth != "number"){
7065                 stopEl = Roo.getDom(maxDepth);
7066                 maxDepth = 10;
7067             }
7068             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7069                 if(dq.is(p, simpleSelector)){
7070                     return returnEl ? Roo.get(p) : p;
7071                 }
7072                 depth++;
7073                 p = p.parentNode;
7074             }
7075             return null;
7076         },
7077
7078
7079         /**
7080          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7081          * @param {String} selector The simple selector to test
7082          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7083                 search as a number or element (defaults to 10 || document.body)
7084          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7085          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7086          */
7087         findParentNode : function(simpleSelector, maxDepth, returnEl){
7088             var p = Roo.fly(this.dom.parentNode, '_internal');
7089             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7090         },
7091
7092         /**
7093          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7094          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7095          * @param {String} selector The simple selector to test
7096          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7097                 search as a number or element (defaults to 10 || document.body)
7098          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7099          */
7100         up : function(simpleSelector, maxDepth){
7101             return this.findParentNode(simpleSelector, maxDepth, true);
7102         },
7103
7104
7105
7106         /**
7107          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7108          * @param {String} selector The simple selector to test
7109          * @return {Boolean} True if this element matches the selector, else false
7110          */
7111         is : function(simpleSelector){
7112             return Roo.DomQuery.is(this.dom, simpleSelector);
7113         },
7114
7115         /**
7116          * Perform animation on this element.
7117          * @param {Object} args The YUI animation control args
7118          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7119          * @param {Function} onComplete (optional) Function to call when animation completes
7120          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7121          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7122          * @return {Roo.Element} this
7123          */
7124         animate : function(args, duration, onComplete, easing, animType){
7125             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7126             return this;
7127         },
7128
7129         /*
7130          * @private Internal animation call
7131          */
7132         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7133             animType = animType || 'run';
7134             opt = opt || {};
7135             var anim = Roo.lib.Anim[animType](
7136                 this.dom, args,
7137                 (opt.duration || defaultDur) || .35,
7138                 (opt.easing || defaultEase) || 'easeOut',
7139                 function(){
7140                     Roo.callback(cb, this);
7141                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7142                 },
7143                 this
7144             );
7145             opt.anim = anim;
7146             return anim;
7147         },
7148
7149         // private legacy anim prep
7150         preanim : function(a, i){
7151             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7152         },
7153
7154         /**
7155          * Removes worthless text nodes
7156          * @param {Boolean} forceReclean (optional) By default the element
7157          * keeps track if it has been cleaned already so
7158          * you can call this over and over. However, if you update the element and
7159          * need to force a reclean, you can pass true.
7160          */
7161         clean : function(forceReclean){
7162             if(this.isCleaned && forceReclean !== true){
7163                 return this;
7164             }
7165             var ns = /\S/;
7166             var d = this.dom, n = d.firstChild, ni = -1;
7167             while(n){
7168                 var nx = n.nextSibling;
7169                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7170                     d.removeChild(n);
7171                 }else{
7172                     n.nodeIndex = ++ni;
7173                 }
7174                 n = nx;
7175             }
7176             this.isCleaned = true;
7177             return this;
7178         },
7179
7180         // private
7181         calcOffsetsTo : function(el){
7182             el = Roo.get(el);
7183             var d = el.dom;
7184             var restorePos = false;
7185             if(el.getStyle('position') == 'static'){
7186                 el.position('relative');
7187                 restorePos = true;
7188             }
7189             var x = 0, y =0;
7190             var op = this.dom;
7191             while(op && op != d && op.tagName != 'HTML'){
7192                 x+= op.offsetLeft;
7193                 y+= op.offsetTop;
7194                 op = op.offsetParent;
7195             }
7196             if(restorePos){
7197                 el.position('static');
7198             }
7199             return [x, y];
7200         },
7201
7202         /**
7203          * Scrolls this element into view within the passed container.
7204          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7205          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7206          * @return {Roo.Element} this
7207          */
7208         scrollIntoView : function(container, hscroll){
7209             var c = Roo.getDom(container) || document.body;
7210             var el = this.dom;
7211
7212             var o = this.calcOffsetsTo(c),
7213                 l = o[0],
7214                 t = o[1],
7215                 b = t+el.offsetHeight,
7216                 r = l+el.offsetWidth;
7217
7218             var ch = c.clientHeight;
7219             var ct = parseInt(c.scrollTop, 10);
7220             var cl = parseInt(c.scrollLeft, 10);
7221             var cb = ct + ch;
7222             var cr = cl + c.clientWidth;
7223
7224             if(t < ct){
7225                 c.scrollTop = t;
7226             }else if(b > cb){
7227                 c.scrollTop = b-ch;
7228             }
7229
7230             if(hscroll !== false){
7231                 if(l < cl){
7232                     c.scrollLeft = l;
7233                 }else if(r > cr){
7234                     c.scrollLeft = r-c.clientWidth;
7235                 }
7236             }
7237             return this;
7238         },
7239
7240         // private
7241         scrollChildIntoView : function(child, hscroll){
7242             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7243         },
7244
7245         /**
7246          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7247          * the new height may not be available immediately.
7248          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7249          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7250          * @param {Function} onComplete (optional) Function to call when animation completes
7251          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7252          * @return {Roo.Element} this
7253          */
7254         autoHeight : function(animate, duration, onComplete, easing){
7255             var oldHeight = this.getHeight();
7256             this.clip();
7257             this.setHeight(1); // force clipping
7258             setTimeout(function(){
7259                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7260                 if(!animate){
7261                     this.setHeight(height);
7262                     this.unclip();
7263                     if(typeof onComplete == "function"){
7264                         onComplete();
7265                     }
7266                 }else{
7267                     this.setHeight(oldHeight); // restore original height
7268                     this.setHeight(height, animate, duration, function(){
7269                         this.unclip();
7270                         if(typeof onComplete == "function") onComplete();
7271                     }.createDelegate(this), easing);
7272                 }
7273             }.createDelegate(this), 0);
7274             return this;
7275         },
7276
7277         /**
7278          * Returns true if this element is an ancestor of the passed element
7279          * @param {HTMLElement/String} el The element to check
7280          * @return {Boolean} True if this element is an ancestor of el, else false
7281          */
7282         contains : function(el){
7283             if(!el){return false;}
7284             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7285         },
7286
7287         /**
7288          * Checks whether the element is currently visible using both visibility and display properties.
7289          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7290          * @return {Boolean} True if the element is currently visible, else false
7291          */
7292         isVisible : function(deep) {
7293             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7294             if(deep !== true || !vis){
7295                 return vis;
7296             }
7297             var p = this.dom.parentNode;
7298             while(p && p.tagName.toLowerCase() != "body"){
7299                 if(!Roo.fly(p, '_isVisible').isVisible()){
7300                     return false;
7301                 }
7302                 p = p.parentNode;
7303             }
7304             return true;
7305         },
7306
7307         /**
7308          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7309          * @param {String} selector The CSS selector
7310          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7311          * @return {CompositeElement/CompositeElementLite} The composite element
7312          */
7313         select : function(selector, unique){
7314             return El.select(selector, unique, this.dom);
7315         },
7316
7317         /**
7318          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @return {Array} An array of the matched nodes
7321          */
7322         query : function(selector, unique){
7323             return Roo.DomQuery.select(selector, this.dom);
7324         },
7325
7326         /**
7327          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7328          * @param {String} selector The CSS selector
7329          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7330          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7331          */
7332         child : function(selector, returnDom){
7333             var n = Roo.DomQuery.selectNode(selector, this.dom);
7334             return returnDom ? n : Roo.get(n);
7335         },
7336
7337         /**
7338          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7339          * @param {String} selector The CSS selector
7340          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7341          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7342          */
7343         down : function(selector, returnDom){
7344             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7345             return returnDom ? n : Roo.get(n);
7346         },
7347
7348         /**
7349          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7350          * @param {String} group The group the DD object is member of
7351          * @param {Object} config The DD config object
7352          * @param {Object} overrides An object containing methods to override/implement on the DD object
7353          * @return {Roo.dd.DD} The DD object
7354          */
7355         initDD : function(group, config, overrides){
7356             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7357             return Roo.apply(dd, overrides);
7358         },
7359
7360         /**
7361          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7362          * @param {String} group The group the DDProxy object is member of
7363          * @param {Object} config The DDProxy config object
7364          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7365          * @return {Roo.dd.DDProxy} The DDProxy object
7366          */
7367         initDDProxy : function(group, config, overrides){
7368             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7369             return Roo.apply(dd, overrides);
7370         },
7371
7372         /**
7373          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7374          * @param {String} group The group the DDTarget object is member of
7375          * @param {Object} config The DDTarget config object
7376          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7377          * @return {Roo.dd.DDTarget} The DDTarget object
7378          */
7379         initDDTarget : function(group, config, overrides){
7380             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7381             return Roo.apply(dd, overrides);
7382         },
7383
7384         /**
7385          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7386          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7387          * @param {Boolean} visible Whether the element is visible
7388          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7389          * @return {Roo.Element} this
7390          */
7391          setVisible : function(visible, animate){
7392             if(!animate || !A){
7393                 if(this.visibilityMode == El.DISPLAY){
7394                     this.setDisplayed(visible);
7395                 }else{
7396                     this.fixDisplay();
7397                     this.dom.style.visibility = visible ? "visible" : "hidden";
7398                 }
7399             }else{
7400                 // closure for composites
7401                 var dom = this.dom;
7402                 var visMode = this.visibilityMode;
7403                 if(visible){
7404                     this.setOpacity(.01);
7405                     this.setVisible(true);
7406                 }
7407                 this.anim({opacity: { to: (visible?1:0) }},
7408                       this.preanim(arguments, 1),
7409                       null, .35, 'easeIn', function(){
7410                          if(!visible){
7411                              if(visMode == El.DISPLAY){
7412                                  dom.style.display = "none";
7413                              }else{
7414                                  dom.style.visibility = "hidden";
7415                              }
7416                              Roo.get(dom).setOpacity(1);
7417                          }
7418                      });
7419             }
7420             return this;
7421         },
7422
7423         /**
7424          * Returns true if display is not "none"
7425          * @return {Boolean}
7426          */
7427         isDisplayed : function() {
7428             return this.getStyle("display") != "none";
7429         },
7430
7431         /**
7432          * Toggles the element's visibility or display, depending on visibility mode.
7433          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7434          * @return {Roo.Element} this
7435          */
7436         toggle : function(animate){
7437             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7438             return this;
7439         },
7440
7441         /**
7442          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7443          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7444          * @return {Roo.Element} this
7445          */
7446         setDisplayed : function(value) {
7447             if(typeof value == "boolean"){
7448                value = value ? this.originalDisplay : "none";
7449             }
7450             this.setStyle("display", value);
7451             return this;
7452         },
7453
7454         /**
7455          * Tries to focus the element. Any exceptions are caught and ignored.
7456          * @return {Roo.Element} this
7457          */
7458         focus : function() {
7459             try{
7460                 this.dom.focus();
7461             }catch(e){}
7462             return this;
7463         },
7464
7465         /**
7466          * Tries to blur the element. Any exceptions are caught and ignored.
7467          * @return {Roo.Element} this
7468          */
7469         blur : function() {
7470             try{
7471                 this.dom.blur();
7472             }catch(e){}
7473             return this;
7474         },
7475
7476         /**
7477          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7478          * @param {String/Array} className The CSS class to add, or an array of classes
7479          * @return {Roo.Element} this
7480          */
7481         addClass : function(className){
7482             if(className instanceof Array){
7483                 for(var i = 0, len = className.length; i < len; i++) {
7484                     this.addClass(className[i]);
7485                 }
7486             }else{
7487                 if(className && !this.hasClass(className)){
7488                     this.dom.className = this.dom.className + " " + className;
7489                 }
7490             }
7491             return this;
7492         },
7493
7494         /**
7495          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7496          * @param {String/Array} className The CSS class to add, or an array of classes
7497          * @return {Roo.Element} this
7498          */
7499         radioClass : function(className){
7500             var siblings = this.dom.parentNode.childNodes;
7501             for(var i = 0; i < siblings.length; i++) {
7502                 var s = siblings[i];
7503                 if(s.nodeType == 1){
7504                     Roo.get(s).removeClass(className);
7505                 }
7506             }
7507             this.addClass(className);
7508             return this;
7509         },
7510
7511         /**
7512          * Removes one or more CSS classes from the element.
7513          * @param {String/Array} className The CSS class to remove, or an array of classes
7514          * @return {Roo.Element} this
7515          */
7516         removeClass : function(className){
7517             if(!className || !this.dom.className){
7518                 return this;
7519             }
7520             if(className instanceof Array){
7521                 for(var i = 0, len = className.length; i < len; i++) {
7522                     this.removeClass(className[i]);
7523                 }
7524             }else{
7525                 if(this.hasClass(className)){
7526                     var re = this.classReCache[className];
7527                     if (!re) {
7528                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7529                        this.classReCache[className] = re;
7530                     }
7531                     this.dom.className =
7532                         this.dom.className.replace(re, " ");
7533                 }
7534             }
7535             return this;
7536         },
7537
7538         // private
7539         classReCache: {},
7540
7541         /**
7542          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7543          * @param {String} className The CSS class to toggle
7544          * @return {Roo.Element} this
7545          */
7546         toggleClass : function(className){
7547             if(this.hasClass(className)){
7548                 this.removeClass(className);
7549             }else{
7550                 this.addClass(className);
7551             }
7552             return this;
7553         },
7554
7555         /**
7556          * Checks if the specified CSS class exists on this element's DOM node.
7557          * @param {String} className The CSS class to check for
7558          * @return {Boolean} True if the class exists, else false
7559          */
7560         hasClass : function(className){
7561             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7562         },
7563
7564         /**
7565          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7566          * @param {String} oldClassName The CSS class to replace
7567          * @param {String} newClassName The replacement CSS class
7568          * @return {Roo.Element} this
7569          */
7570         replaceClass : function(oldClassName, newClassName){
7571             this.removeClass(oldClassName);
7572             this.addClass(newClassName);
7573             return this;
7574         },
7575
7576         /**
7577          * Returns an object with properties matching the styles requested.
7578          * For example, el.getStyles('color', 'font-size', 'width') might return
7579          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7580          * @param {String} style1 A style name
7581          * @param {String} style2 A style name
7582          * @param {String} etc.
7583          * @return {Object} The style object
7584          */
7585         getStyles : function(){
7586             var a = arguments, len = a.length, r = {};
7587             for(var i = 0; i < len; i++){
7588                 r[a[i]] = this.getStyle(a[i]);
7589             }
7590             return r;
7591         },
7592
7593         /**
7594          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7595          * @param {String} property The style property whose value is returned.
7596          * @return {String} The current value of the style property for this element.
7597          */
7598         getStyle : function(){
7599             return view && view.getComputedStyle ?
7600                 function(prop){
7601                     var el = this.dom, v, cs, camel;
7602                     if(prop == 'float'){
7603                         prop = "cssFloat";
7604                     }
7605                     if(el.style && (v = el.style[prop])){
7606                         return v;
7607                     }
7608                     if(cs = view.getComputedStyle(el, "")){
7609                         if(!(camel = propCache[prop])){
7610                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7611                         }
7612                         return cs[camel];
7613                     }
7614                     return null;
7615                 } :
7616                 function(prop){
7617                     var el = this.dom, v, cs, camel;
7618                     if(prop == 'opacity'){
7619                         if(typeof el.style.filter == 'string'){
7620                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7621                             if(m){
7622                                 var fv = parseFloat(m[1]);
7623                                 if(!isNaN(fv)){
7624                                     return fv ? fv / 100 : 0;
7625                                 }
7626                             }
7627                         }
7628                         return 1;
7629                     }else if(prop == 'float'){
7630                         prop = "styleFloat";
7631                     }
7632                     if(!(camel = propCache[prop])){
7633                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7634                     }
7635                     if(v = el.style[camel]){
7636                         return v;
7637                     }
7638                     if(cs = el.currentStyle){
7639                         return cs[camel];
7640                     }
7641                     return null;
7642                 };
7643         }(),
7644
7645         /**
7646          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7647          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7648          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7649          * @return {Roo.Element} this
7650          */
7651         setStyle : function(prop, value){
7652             if(typeof prop == "string"){
7653                 
7654                 if (prop == 'float') {
7655                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7656                     return this;
7657                 }
7658                 
7659                 var camel;
7660                 if(!(camel = propCache[prop])){
7661                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7662                 }
7663                 
7664                 if(camel == 'opacity') {
7665                     this.setOpacity(value);
7666                 }else{
7667                     this.dom.style[camel] = value;
7668                 }
7669             }else{
7670                 for(var style in prop){
7671                     if(typeof prop[style] != "function"){
7672                        this.setStyle(style, prop[style]);
7673                     }
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         /**
7680          * More flexible version of {@link #setStyle} for setting style properties.
7681          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7682          * a function which returns such a specification.
7683          * @return {Roo.Element} this
7684          */
7685         applyStyles : function(style){
7686             Roo.DomHelper.applyStyles(this.dom, style);
7687             return this;
7688         },
7689
7690         /**
7691           * 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).
7692           * @return {Number} The X position of the element
7693           */
7694         getX : function(){
7695             return D.getX(this.dom);
7696         },
7697
7698         /**
7699           * 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).
7700           * @return {Number} The Y position of the element
7701           */
7702         getY : function(){
7703             return D.getY(this.dom);
7704         },
7705
7706         /**
7707           * 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).
7708           * @return {Array} The XY position of the element
7709           */
7710         getXY : function(){
7711             return D.getXY(this.dom);
7712         },
7713
7714         /**
7715          * 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).
7716          * @param {Number} The X position of the element
7717          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setX : function(x, animate){
7721             if(!animate || !A){
7722                 D.setX(this.dom, x);
7723             }else{
7724                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7725             }
7726             return this;
7727         },
7728
7729         /**
7730          * 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).
7731          * @param {Number} The Y position of the element
7732          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7733          * @return {Roo.Element} this
7734          */
7735         setY : function(y, animate){
7736             if(!animate || !A){
7737                 D.setY(this.dom, y);
7738             }else{
7739                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7740             }
7741             return this;
7742         },
7743
7744         /**
7745          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7746          * @param {String} left The left CSS property value
7747          * @return {Roo.Element} this
7748          */
7749         setLeft : function(left){
7750             this.setStyle("left", this.addUnits(left));
7751             return this;
7752         },
7753
7754         /**
7755          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7756          * @param {String} top The top CSS property value
7757          * @return {Roo.Element} this
7758          */
7759         setTop : function(top){
7760             this.setStyle("top", this.addUnits(top));
7761             return this;
7762         },
7763
7764         /**
7765          * Sets the element's CSS right style.
7766          * @param {String} right The right CSS property value
7767          * @return {Roo.Element} this
7768          */
7769         setRight : function(right){
7770             this.setStyle("right", this.addUnits(right));
7771             return this;
7772         },
7773
7774         /**
7775          * Sets the element's CSS bottom style.
7776          * @param {String} bottom The bottom CSS property value
7777          * @return {Roo.Element} this
7778          */
7779         setBottom : function(bottom){
7780             this.setStyle("bottom", this.addUnits(bottom));
7781             return this;
7782         },
7783
7784         /**
7785          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7786          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7787          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7788          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7789          * @return {Roo.Element} this
7790          */
7791         setXY : function(pos, animate){
7792             if(!animate || !A){
7793                 D.setXY(this.dom, pos);
7794             }else{
7795                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7796             }
7797             return this;
7798         },
7799
7800         /**
7801          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7802          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7803          * @param {Number} x X value for new position (coordinates are page-based)
7804          * @param {Number} y Y value for new position (coordinates are page-based)
7805          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7806          * @return {Roo.Element} this
7807          */
7808         setLocation : function(x, y, animate){
7809             this.setXY([x, y], this.preanim(arguments, 2));
7810             return this;
7811         },
7812
7813         /**
7814          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7815          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7816          * @param {Number} x X value for new position (coordinates are page-based)
7817          * @param {Number} y Y value for new position (coordinates are page-based)
7818          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821         moveTo : function(x, y, animate){
7822             this.setXY([x, y], this.preanim(arguments, 2));
7823             return this;
7824         },
7825
7826         /**
7827          * Returns the region of the given element.
7828          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7829          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7830          */
7831         getRegion : function(){
7832             return D.getRegion(this.dom);
7833         },
7834
7835         /**
7836          * Returns the offset height of the element
7837          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7838          * @return {Number} The element's height
7839          */
7840         getHeight : function(contentHeight){
7841             var h = this.dom.offsetHeight || 0;
7842             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7843         },
7844
7845         /**
7846          * Returns the offset width of the element
7847          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7848          * @return {Number} The element's width
7849          */
7850         getWidth : function(contentWidth){
7851             var w = this.dom.offsetWidth || 0;
7852             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7853         },
7854
7855         /**
7856          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7857          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7858          * if a height has not been set using CSS.
7859          * @return {Number}
7860          */
7861         getComputedHeight : function(){
7862             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7863             if(!h){
7864                 h = parseInt(this.getStyle('height'), 10) || 0;
7865                 if(!this.isBorderBox()){
7866                     h += this.getFrameWidth('tb');
7867                 }
7868             }
7869             return h;
7870         },
7871
7872         /**
7873          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7874          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7875          * if a width has not been set using CSS.
7876          * @return {Number}
7877          */
7878         getComputedWidth : function(){
7879             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7880             if(!w){
7881                 w = parseInt(this.getStyle('width'), 10) || 0;
7882                 if(!this.isBorderBox()){
7883                     w += this.getFrameWidth('lr');
7884                 }
7885             }
7886             return w;
7887         },
7888
7889         /**
7890          * Returns the size of the element.
7891          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7892          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7893          */
7894         getSize : function(contentSize){
7895             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7896         },
7897
7898         /**
7899          * Returns the width and height of the viewport.
7900          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7901          */
7902         getViewSize : function(){
7903             var d = this.dom, doc = document, aw = 0, ah = 0;
7904             if(d == doc || d == doc.body){
7905                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7906             }else{
7907                 return {
7908                     width : d.clientWidth,
7909                     height: d.clientHeight
7910                 };
7911             }
7912         },
7913
7914         /**
7915          * Returns the value of the "value" attribute
7916          * @param {Boolean} asNumber true to parse the value as a number
7917          * @return {String/Number}
7918          */
7919         getValue : function(asNumber){
7920             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7921         },
7922
7923         // private
7924         adjustWidth : function(width){
7925             if(typeof width == "number"){
7926                 if(this.autoBoxAdjust && !this.isBorderBox()){
7927                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7928                 }
7929                 if(width < 0){
7930                     width = 0;
7931                 }
7932             }
7933             return width;
7934         },
7935
7936         // private
7937         adjustHeight : function(height){
7938             if(typeof height == "number"){
7939                if(this.autoBoxAdjust && !this.isBorderBox()){
7940                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7941                }
7942                if(height < 0){
7943                    height = 0;
7944                }
7945             }
7946             return height;
7947         },
7948
7949         /**
7950          * Set the width of the element
7951          * @param {Number} width The new width
7952          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7953          * @return {Roo.Element} this
7954          */
7955         setWidth : function(width, animate){
7956             width = this.adjustWidth(width);
7957             if(!animate || !A){
7958                 this.dom.style.width = this.addUnits(width);
7959             }else{
7960                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7961             }
7962             return this;
7963         },
7964
7965         /**
7966          * Set the height of the element
7967          * @param {Number} height The new height
7968          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7969          * @return {Roo.Element} this
7970          */
7971          setHeight : function(height, animate){
7972             height = this.adjustHeight(height);
7973             if(!animate || !A){
7974                 this.dom.style.height = this.addUnits(height);
7975             }else{
7976                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7977             }
7978             return this;
7979         },
7980
7981         /**
7982          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7983          * @param {Number} width The new width
7984          * @param {Number} height The new height
7985          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7986          * @return {Roo.Element} this
7987          */
7988          setSize : function(width, height, animate){
7989             if(typeof width == "object"){ // in case of object from getSize()
7990                 height = width.height; width = width.width;
7991             }
7992             width = this.adjustWidth(width); height = this.adjustHeight(height);
7993             if(!animate || !A){
7994                 this.dom.style.width = this.addUnits(width);
7995                 this.dom.style.height = this.addUnits(height);
7996             }else{
7997                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7998             }
7999             return this;
8000         },
8001
8002         /**
8003          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8004          * @param {Number} x X value for new position (coordinates are page-based)
8005          * @param {Number} y Y value for new position (coordinates are page-based)
8006          * @param {Number} width The new width
8007          * @param {Number} height The new height
8008          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8009          * @return {Roo.Element} this
8010          */
8011         setBounds : function(x, y, width, height, animate){
8012             if(!animate || !A){
8013                 this.setSize(width, height);
8014                 this.setLocation(x, y);
8015             }else{
8016                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8017                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8018                               this.preanim(arguments, 4), 'motion');
8019             }
8020             return this;
8021         },
8022
8023         /**
8024          * 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.
8025          * @param {Roo.lib.Region} region The region to fill
8026          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setRegion : function(region, animate){
8030             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8031             return this;
8032         },
8033
8034         /**
8035          * Appends an event handler
8036          *
8037          * @param {String}   eventName     The type of event to append
8038          * @param {Function} fn        The method the event invokes
8039          * @param {Object} scope       (optional) The scope (this object) of the fn
8040          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8041          */
8042         addListener : function(eventName, fn, scope, options){
8043             if (this.dom) {
8044                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8045             }
8046         },
8047
8048         /**
8049          * Removes an event handler from this element
8050          * @param {String} eventName the type of event to remove
8051          * @param {Function} fn the method the event invokes
8052          * @return {Roo.Element} this
8053          */
8054         removeListener : function(eventName, fn){
8055             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8056             return this;
8057         },
8058
8059         /**
8060          * Removes all previous added listeners from this element
8061          * @return {Roo.Element} this
8062          */
8063         removeAllListeners : function(){
8064             E.purgeElement(this.dom);
8065             return this;
8066         },
8067
8068         relayEvent : function(eventName, observable){
8069             this.on(eventName, function(e){
8070                 observable.fireEvent(eventName, e);
8071             });
8072         },
8073
8074         /**
8075          * Set the opacity of the element
8076          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8077          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8078          * @return {Roo.Element} this
8079          */
8080          setOpacity : function(opacity, animate){
8081             if(!animate || !A){
8082                 var s = this.dom.style;
8083                 if(Roo.isIE){
8084                     s.zoom = 1;
8085                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8086                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8087                 }else{
8088                     s.opacity = opacity;
8089                 }
8090             }else{
8091                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8092             }
8093             return this;
8094         },
8095
8096         /**
8097          * Gets the left X coordinate
8098          * @param {Boolean} local True to get the local css position instead of page coordinate
8099          * @return {Number}
8100          */
8101         getLeft : function(local){
8102             if(!local){
8103                 return this.getX();
8104             }else{
8105                 return parseInt(this.getStyle("left"), 10) || 0;
8106             }
8107         },
8108
8109         /**
8110          * Gets the right X coordinate of the element (element X position + element width)
8111          * @param {Boolean} local True to get the local css position instead of page coordinate
8112          * @return {Number}
8113          */
8114         getRight : function(local){
8115             if(!local){
8116                 return this.getX() + this.getWidth();
8117             }else{
8118                 return (this.getLeft(true) + this.getWidth()) || 0;
8119             }
8120         },
8121
8122         /**
8123          * Gets the top Y coordinate
8124          * @param {Boolean} local True to get the local css position instead of page coordinate
8125          * @return {Number}
8126          */
8127         getTop : function(local) {
8128             if(!local){
8129                 return this.getY();
8130             }else{
8131                 return parseInt(this.getStyle("top"), 10) || 0;
8132             }
8133         },
8134
8135         /**
8136          * Gets the bottom Y coordinate of the element (element Y position + element height)
8137          * @param {Boolean} local True to get the local css position instead of page coordinate
8138          * @return {Number}
8139          */
8140         getBottom : function(local){
8141             if(!local){
8142                 return this.getY() + this.getHeight();
8143             }else{
8144                 return (this.getTop(true) + this.getHeight()) || 0;
8145             }
8146         },
8147
8148         /**
8149         * Initializes positioning on this element. If a desired position is not passed, it will make the
8150         * the element positioned relative IF it is not already positioned.
8151         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8152         * @param {Number} zIndex (optional) The zIndex to apply
8153         * @param {Number} x (optional) Set the page X position
8154         * @param {Number} y (optional) Set the page Y position
8155         */
8156         position : function(pos, zIndex, x, y){
8157             if(!pos){
8158                if(this.getStyle('position') == 'static'){
8159                    this.setStyle('position', 'relative');
8160                }
8161             }else{
8162                 this.setStyle("position", pos);
8163             }
8164             if(zIndex){
8165                 this.setStyle("z-index", zIndex);
8166             }
8167             if(x !== undefined && y !== undefined){
8168                 this.setXY([x, y]);
8169             }else if(x !== undefined){
8170                 this.setX(x);
8171             }else if(y !== undefined){
8172                 this.setY(y);
8173             }
8174         },
8175
8176         /**
8177         * Clear positioning back to the default when the document was loaded
8178         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8179         * @return {Roo.Element} this
8180          */
8181         clearPositioning : function(value){
8182             value = value ||'';
8183             this.setStyle({
8184                 "left": value,
8185                 "right": value,
8186                 "top": value,
8187                 "bottom": value,
8188                 "z-index": "",
8189                 "position" : "static"
8190             });
8191             return this;
8192         },
8193
8194         /**
8195         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8196         * snapshot before performing an update and then restoring the element.
8197         * @return {Object}
8198         */
8199         getPositioning : function(){
8200             var l = this.getStyle("left");
8201             var t = this.getStyle("top");
8202             return {
8203                 "position" : this.getStyle("position"),
8204                 "left" : l,
8205                 "right" : l ? "" : this.getStyle("right"),
8206                 "top" : t,
8207                 "bottom" : t ? "" : this.getStyle("bottom"),
8208                 "z-index" : this.getStyle("z-index")
8209             };
8210         },
8211
8212         /**
8213          * Gets the width of the border(s) for the specified side(s)
8214          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8215          * passing lr would get the border (l)eft width + the border (r)ight width.
8216          * @return {Number} The width of the sides passed added together
8217          */
8218         getBorderWidth : function(side){
8219             return this.addStyles(side, El.borders);
8220         },
8221
8222         /**
8223          * Gets the width of the padding(s) for the specified side(s)
8224          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8225          * passing lr would get the padding (l)eft + the padding (r)ight.
8226          * @return {Number} The padding of the sides passed added together
8227          */
8228         getPadding : function(side){
8229             return this.addStyles(side, El.paddings);
8230         },
8231
8232         /**
8233         * Set positioning with an object returned by getPositioning().
8234         * @param {Object} posCfg
8235         * @return {Roo.Element} this
8236          */
8237         setPositioning : function(pc){
8238             this.applyStyles(pc);
8239             if(pc.right == "auto"){
8240                 this.dom.style.right = "";
8241             }
8242             if(pc.bottom == "auto"){
8243                 this.dom.style.bottom = "";
8244             }
8245             return this;
8246         },
8247
8248         // private
8249         fixDisplay : function(){
8250             if(this.getStyle("display") == "none"){
8251                 this.setStyle("visibility", "hidden");
8252                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8253                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8254                     this.setStyle("display", "block");
8255                 }
8256             }
8257         },
8258
8259         /**
8260          * Quick set left and top adding default units
8261          * @param {String} left The left CSS property value
8262          * @param {String} top The top CSS property value
8263          * @return {Roo.Element} this
8264          */
8265          setLeftTop : function(left, top){
8266             this.dom.style.left = this.addUnits(left);
8267             this.dom.style.top = this.addUnits(top);
8268             return this;
8269         },
8270
8271         /**
8272          * Move this element relative to its current position.
8273          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8274          * @param {Number} distance How far to move the element in pixels
8275          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8276          * @return {Roo.Element} this
8277          */
8278          move : function(direction, distance, animate){
8279             var xy = this.getXY();
8280             direction = direction.toLowerCase();
8281             switch(direction){
8282                 case "l":
8283                 case "left":
8284                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8285                     break;
8286                case "r":
8287                case "right":
8288                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8289                     break;
8290                case "t":
8291                case "top":
8292                case "up":
8293                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8294                     break;
8295                case "b":
8296                case "bottom":
8297                case "down":
8298                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8299                     break;
8300             }
8301             return this;
8302         },
8303
8304         /**
8305          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8306          * @return {Roo.Element} this
8307          */
8308         clip : function(){
8309             if(!this.isClipped){
8310                this.isClipped = true;
8311                this.originalClip = {
8312                    "o": this.getStyle("overflow"),
8313                    "x": this.getStyle("overflow-x"),
8314                    "y": this.getStyle("overflow-y")
8315                };
8316                this.setStyle("overflow", "hidden");
8317                this.setStyle("overflow-x", "hidden");
8318                this.setStyle("overflow-y", "hidden");
8319             }
8320             return this;
8321         },
8322
8323         /**
8324          *  Return clipping (overflow) to original clipping before clip() was called
8325          * @return {Roo.Element} this
8326          */
8327         unclip : function(){
8328             if(this.isClipped){
8329                 this.isClipped = false;
8330                 var o = this.originalClip;
8331                 if(o.o){this.setStyle("overflow", o.o);}
8332                 if(o.x){this.setStyle("overflow-x", o.x);}
8333                 if(o.y){this.setStyle("overflow-y", o.y);}
8334             }
8335             return this;
8336         },
8337
8338
8339         /**
8340          * Gets the x,y coordinates specified by the anchor position on the element.
8341          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8342          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8343          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8344          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8345          * @return {Array} [x, y] An array containing the element's x and y coordinates
8346          */
8347         getAnchorXY : function(anchor, local, s){
8348             //Passing a different size is useful for pre-calculating anchors,
8349             //especially for anchored animations that change the el size.
8350
8351             var w, h, vp = false;
8352             if(!s){
8353                 var d = this.dom;
8354                 if(d == document.body || d == document){
8355                     vp = true;
8356                     w = D.getViewWidth(); h = D.getViewHeight();
8357                 }else{
8358                     w = this.getWidth(); h = this.getHeight();
8359                 }
8360             }else{
8361                 w = s.width;  h = s.height;
8362             }
8363             var x = 0, y = 0, r = Math.round;
8364             switch((anchor || "tl").toLowerCase()){
8365                 case "c":
8366                     x = r(w*.5);
8367                     y = r(h*.5);
8368                 break;
8369                 case "t":
8370                     x = r(w*.5);
8371                     y = 0;
8372                 break;
8373                 case "l":
8374                     x = 0;
8375                     y = r(h*.5);
8376                 break;
8377                 case "r":
8378                     x = w;
8379                     y = r(h*.5);
8380                 break;
8381                 case "b":
8382                     x = r(w*.5);
8383                     y = h;
8384                 break;
8385                 case "tl":
8386                     x = 0;
8387                     y = 0;
8388                 break;
8389                 case "bl":
8390                     x = 0;
8391                     y = h;
8392                 break;
8393                 case "br":
8394                     x = w;
8395                     y = h;
8396                 break;
8397                 case "tr":
8398                     x = w;
8399                     y = 0;
8400                 break;
8401             }
8402             if(local === true){
8403                 return [x, y];
8404             }
8405             if(vp){
8406                 var sc = this.getScroll();
8407                 return [x + sc.left, y + sc.top];
8408             }
8409             //Add the element's offset xy
8410             var o = this.getXY();
8411             return [x+o[0], y+o[1]];
8412         },
8413
8414         /**
8415          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8416          * supported position values.
8417          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8418          * @param {String} position The position to align to.
8419          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8420          * @return {Array} [x, y]
8421          */
8422         getAlignToXY : function(el, p, o){
8423             el = Roo.get(el);
8424             var d = this.dom;
8425             if(!el.dom){
8426                 throw "Element.alignTo with an element that doesn't exist";
8427             }
8428             var c = false; //constrain to viewport
8429             var p1 = "", p2 = "";
8430             o = o || [0,0];
8431
8432             if(!p){
8433                 p = "tl-bl";
8434             }else if(p == "?"){
8435                 p = "tl-bl?";
8436             }else if(p.indexOf("-") == -1){
8437                 p = "tl-" + p;
8438             }
8439             p = p.toLowerCase();
8440             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8441             if(!m){
8442                throw "Element.alignTo with an invalid alignment " + p;
8443             }
8444             p1 = m[1]; p2 = m[2]; c = !!m[3];
8445
8446             //Subtract the aligned el's internal xy from the target's offset xy
8447             //plus custom offset to get the aligned el's new offset xy
8448             var a1 = this.getAnchorXY(p1, true);
8449             var a2 = el.getAnchorXY(p2, false);
8450             var x = a2[0] - a1[0] + o[0];
8451             var y = a2[1] - a1[1] + o[1];
8452             if(c){
8453                 //constrain the aligned el to viewport if necessary
8454                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8455                 // 5px of margin for ie
8456                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8457
8458                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8459                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8460                 //otherwise swap the aligned el to the opposite border of the target.
8461                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8462                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8463                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8464                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8465
8466                var doc = document;
8467                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8468                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8469
8470                if((x+w) > dw + scrollX){
8471                     x = swapX ? r.left-w : dw+scrollX-w;
8472                 }
8473                if(x < scrollX){
8474                    x = swapX ? r.right : scrollX;
8475                }
8476                if((y+h) > dh + scrollY){
8477                     y = swapY ? r.top-h : dh+scrollY-h;
8478                 }
8479                if (y < scrollY){
8480                    y = swapY ? r.bottom : scrollY;
8481                }
8482             }
8483             return [x,y];
8484         },
8485
8486         // private
8487         getConstrainToXY : function(){
8488             var os = {top:0, left:0, bottom:0, right: 0};
8489
8490             return function(el, local, offsets, proposedXY){
8491                 el = Roo.get(el);
8492                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8493
8494                 var vw, vh, vx = 0, vy = 0;
8495                 if(el.dom == document.body || el.dom == document){
8496                     vw = Roo.lib.Dom.getViewWidth();
8497                     vh = Roo.lib.Dom.getViewHeight();
8498                 }else{
8499                     vw = el.dom.clientWidth;
8500                     vh = el.dom.clientHeight;
8501                     if(!local){
8502                         var vxy = el.getXY();
8503                         vx = vxy[0];
8504                         vy = vxy[1];
8505                     }
8506                 }
8507
8508                 var s = el.getScroll();
8509
8510                 vx += offsets.left + s.left;
8511                 vy += offsets.top + s.top;
8512
8513                 vw -= offsets.right;
8514                 vh -= offsets.bottom;
8515
8516                 var vr = vx+vw;
8517                 var vb = vy+vh;
8518
8519                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8520                 var x = xy[0], y = xy[1];
8521                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8522
8523                 // only move it if it needs it
8524                 var moved = false;
8525
8526                 // first validate right/bottom
8527                 if((x + w) > vr){
8528                     x = vr - w;
8529                     moved = true;
8530                 }
8531                 if((y + h) > vb){
8532                     y = vb - h;
8533                     moved = true;
8534                 }
8535                 // then make sure top/left isn't negative
8536                 if(x < vx){
8537                     x = vx;
8538                     moved = true;
8539                 }
8540                 if(y < vy){
8541                     y = vy;
8542                     moved = true;
8543                 }
8544                 return moved ? [x, y] : false;
8545             };
8546         }(),
8547
8548         // private
8549         adjustForConstraints : function(xy, parent, offsets){
8550             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8551         },
8552
8553         /**
8554          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8555          * document it aligns it to the viewport.
8556          * The position parameter is optional, and can be specified in any one of the following formats:
8557          * <ul>
8558          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8559          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8560          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8561          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8562          *   <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
8563          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8564          * </ul>
8565          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8566          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8567          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8568          * that specified in order to enforce the viewport constraints.
8569          * Following are all of the supported anchor positions:
8570     <pre>
8571     Value  Description
8572     -----  -----------------------------
8573     tl     The top left corner (default)
8574     t      The center of the top edge
8575     tr     The top right corner
8576     l      The center of the left edge
8577     c      In the center of the element
8578     r      The center of the right edge
8579     bl     The bottom left corner
8580     b      The center of the bottom edge
8581     br     The bottom right corner
8582     </pre>
8583     Example Usage:
8584     <pre><code>
8585     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8586     el.alignTo("other-el");
8587
8588     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8589     el.alignTo("other-el", "tr?");
8590
8591     // align the bottom right corner of el with the center left edge of other-el
8592     el.alignTo("other-el", "br-l?");
8593
8594     // align the center of el with the bottom left corner of other-el and
8595     // adjust the x position by -6 pixels (and the y position by 0)
8596     el.alignTo("other-el", "c-bl", [-6, 0]);
8597     </code></pre>
8598          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8599          * @param {String} position The position to align to.
8600          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8601          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8602          * @return {Roo.Element} this
8603          */
8604         alignTo : function(element, position, offsets, animate){
8605             var xy = this.getAlignToXY(element, position, offsets);
8606             this.setXY(xy, this.preanim(arguments, 3));
8607             return this;
8608         },
8609
8610         /**
8611          * Anchors an element to another element and realigns it when the window is resized.
8612          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8613          * @param {String} position The position to align to.
8614          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8615          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8616          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8617          * is a number, it is used as the buffer delay (defaults to 50ms).
8618          * @param {Function} callback The function to call after the animation finishes
8619          * @return {Roo.Element} this
8620          */
8621         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8622             var action = function(){
8623                 this.alignTo(el, alignment, offsets, animate);
8624                 Roo.callback(callback, this);
8625             };
8626             Roo.EventManager.onWindowResize(action, this);
8627             var tm = typeof monitorScroll;
8628             if(tm != 'undefined'){
8629                 Roo.EventManager.on(window, 'scroll', action, this,
8630                     {buffer: tm == 'number' ? monitorScroll : 50});
8631             }
8632             action.call(this); // align immediately
8633             return this;
8634         },
8635         /**
8636          * Clears any opacity settings from this element. Required in some cases for IE.
8637          * @return {Roo.Element} this
8638          */
8639         clearOpacity : function(){
8640             if (window.ActiveXObject) {
8641                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8642                     this.dom.style.filter = "";
8643                 }
8644             } else {
8645                 this.dom.style.opacity = "";
8646                 this.dom.style["-moz-opacity"] = "";
8647                 this.dom.style["-khtml-opacity"] = "";
8648             }
8649             return this;
8650         },
8651
8652         /**
8653          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8654          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8655          * @return {Roo.Element} this
8656          */
8657         hide : function(animate){
8658             this.setVisible(false, this.preanim(arguments, 0));
8659             return this;
8660         },
8661
8662         /**
8663         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8664         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8665          * @return {Roo.Element} this
8666          */
8667         show : function(animate){
8668             this.setVisible(true, this.preanim(arguments, 0));
8669             return this;
8670         },
8671
8672         /**
8673          * @private Test if size has a unit, otherwise appends the default
8674          */
8675         addUnits : function(size){
8676             return Roo.Element.addUnits(size, this.defaultUnit);
8677         },
8678
8679         /**
8680          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8681          * @return {Roo.Element} this
8682          */
8683         beginMeasure : function(){
8684             var el = this.dom;
8685             if(el.offsetWidth || el.offsetHeight){
8686                 return this; // offsets work already
8687             }
8688             var changed = [];
8689             var p = this.dom, b = document.body; // start with this element
8690             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8691                 var pe = Roo.get(p);
8692                 if(pe.getStyle('display') == 'none'){
8693                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8694                     p.style.visibility = "hidden";
8695                     p.style.display = "block";
8696                 }
8697                 p = p.parentNode;
8698             }
8699             this._measureChanged = changed;
8700             return this;
8701
8702         },
8703
8704         /**
8705          * Restores displays to before beginMeasure was called
8706          * @return {Roo.Element} this
8707          */
8708         endMeasure : function(){
8709             var changed = this._measureChanged;
8710             if(changed){
8711                 for(var i = 0, len = changed.length; i < len; i++) {
8712                     var r = changed[i];
8713                     r.el.style.visibility = r.visibility;
8714                     r.el.style.display = "none";
8715                 }
8716                 this._measureChanged = null;
8717             }
8718             return this;
8719         },
8720
8721         /**
8722         * Update the innerHTML of this element, optionally searching for and processing scripts
8723         * @param {String} html The new HTML
8724         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8725         * @param {Function} callback For async script loading you can be noticed when the update completes
8726         * @return {Roo.Element} this
8727          */
8728         update : function(html, loadScripts, callback){
8729             if(typeof html == "undefined"){
8730                 html = "";
8731             }
8732             if(loadScripts !== true){
8733                 this.dom.innerHTML = html;
8734                 if(typeof callback == "function"){
8735                     callback();
8736                 }
8737                 return this;
8738             }
8739             var id = Roo.id();
8740             var dom = this.dom;
8741
8742             html += '<span id="' + id + '"></span>';
8743
8744             E.onAvailable(id, function(){
8745                 var hd = document.getElementsByTagName("head")[0];
8746                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8747                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8748                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8749
8750                 var match;
8751                 while(match = re.exec(html)){
8752                     var attrs = match[1];
8753                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8754                     if(srcMatch && srcMatch[2]){
8755                        var s = document.createElement("script");
8756                        s.src = srcMatch[2];
8757                        var typeMatch = attrs.match(typeRe);
8758                        if(typeMatch && typeMatch[2]){
8759                            s.type = typeMatch[2];
8760                        }
8761                        hd.appendChild(s);
8762                     }else if(match[2] && match[2].length > 0){
8763                         if(window.execScript) {
8764                            window.execScript(match[2]);
8765                         } else {
8766                             /**
8767                              * eval:var:id
8768                              * eval:var:dom
8769                              * eval:var:html
8770                              * 
8771                              */
8772                            window.eval(match[2]);
8773                         }
8774                     }
8775                 }
8776                 var el = document.getElementById(id);
8777                 if(el){el.parentNode.removeChild(el);}
8778                 if(typeof callback == "function"){
8779                     callback();
8780                 }
8781             });
8782             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8783             return this;
8784         },
8785
8786         /**
8787          * Direct access to the UpdateManager update() method (takes the same parameters).
8788          * @param {String/Function} url The url for this request or a function to call to get the url
8789          * @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}
8790          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8791          * @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.
8792          * @return {Roo.Element} this
8793          */
8794         load : function(){
8795             var um = this.getUpdateManager();
8796             um.update.apply(um, arguments);
8797             return this;
8798         },
8799
8800         /**
8801         * Gets this element's UpdateManager
8802         * @return {Roo.UpdateManager} The UpdateManager
8803         */
8804         getUpdateManager : function(){
8805             if(!this.updateManager){
8806                 this.updateManager = new Roo.UpdateManager(this);
8807             }
8808             return this.updateManager;
8809         },
8810
8811         /**
8812          * Disables text selection for this element (normalized across browsers)
8813          * @return {Roo.Element} this
8814          */
8815         unselectable : function(){
8816             this.dom.unselectable = "on";
8817             this.swallowEvent("selectstart", true);
8818             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8819             this.addClass("x-unselectable");
8820             return this;
8821         },
8822
8823         /**
8824         * Calculates the x, y to center this element on the screen
8825         * @return {Array} The x, y values [x, y]
8826         */
8827         getCenterXY : function(){
8828             return this.getAlignToXY(document, 'c-c');
8829         },
8830
8831         /**
8832         * Centers the Element in either the viewport, or another Element.
8833         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8834         */
8835         center : function(centerIn){
8836             this.alignTo(centerIn || document, 'c-c');
8837             return this;
8838         },
8839
8840         /**
8841          * Tests various css rules/browsers to determine if this element uses a border box
8842          * @return {Boolean}
8843          */
8844         isBorderBox : function(){
8845             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8846         },
8847
8848         /**
8849          * Return a box {x, y, width, height} that can be used to set another elements
8850          * size/location to match this element.
8851          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8852          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8853          * @return {Object} box An object in the format {x, y, width, height}
8854          */
8855         getBox : function(contentBox, local){
8856             var xy;
8857             if(!local){
8858                 xy = this.getXY();
8859             }else{
8860                 var left = parseInt(this.getStyle("left"), 10) || 0;
8861                 var top = parseInt(this.getStyle("top"), 10) || 0;
8862                 xy = [left, top];
8863             }
8864             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8865             if(!contentBox){
8866                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8867             }else{
8868                 var l = this.getBorderWidth("l")+this.getPadding("l");
8869                 var r = this.getBorderWidth("r")+this.getPadding("r");
8870                 var t = this.getBorderWidth("t")+this.getPadding("t");
8871                 var b = this.getBorderWidth("b")+this.getPadding("b");
8872                 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)};
8873             }
8874             bx.right = bx.x + bx.width;
8875             bx.bottom = bx.y + bx.height;
8876             return bx;
8877         },
8878
8879         /**
8880          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8881          for more information about the sides.
8882          * @param {String} sides
8883          * @return {Number}
8884          */
8885         getFrameWidth : function(sides, onlyContentBox){
8886             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8887         },
8888
8889         /**
8890          * 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.
8891          * @param {Object} box The box to fill {x, y, width, height}
8892          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8893          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8894          * @return {Roo.Element} this
8895          */
8896         setBox : function(box, adjust, animate){
8897             var w = box.width, h = box.height;
8898             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8899                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8900                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8901             }
8902             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8903             return this;
8904         },
8905
8906         /**
8907          * Forces the browser to repaint this element
8908          * @return {Roo.Element} this
8909          */
8910          repaint : function(){
8911             var dom = this.dom;
8912             this.addClass("x-repaint");
8913             setTimeout(function(){
8914                 Roo.get(dom).removeClass("x-repaint");
8915             }, 1);
8916             return this;
8917         },
8918
8919         /**
8920          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8921          * then it returns the calculated width of the sides (see getPadding)
8922          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8923          * @return {Object/Number}
8924          */
8925         getMargins : function(side){
8926             if(!side){
8927                 return {
8928                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8929                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8930                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8931                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8932                 };
8933             }else{
8934                 return this.addStyles(side, El.margins);
8935              }
8936         },
8937
8938         // private
8939         addStyles : function(sides, styles){
8940             var val = 0, v, w;
8941             for(var i = 0, len = sides.length; i < len; i++){
8942                 v = this.getStyle(styles[sides.charAt(i)]);
8943                 if(v){
8944                      w = parseInt(v, 10);
8945                      if(w){ val += w; }
8946                 }
8947             }
8948             return val;
8949         },
8950
8951         /**
8952          * Creates a proxy element of this element
8953          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8954          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8955          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8956          * @return {Roo.Element} The new proxy element
8957          */
8958         createProxy : function(config, renderTo, matchBox){
8959             if(renderTo){
8960                 renderTo = Roo.getDom(renderTo);
8961             }else{
8962                 renderTo = document.body;
8963             }
8964             config = typeof config == "object" ?
8965                 config : {tag : "div", cls: config};
8966             var proxy = Roo.DomHelper.append(renderTo, config, true);
8967             if(matchBox){
8968                proxy.setBox(this.getBox());
8969             }
8970             return proxy;
8971         },
8972
8973         /**
8974          * Puts a mask over this element to disable user interaction. Requires core.css.
8975          * This method can only be applied to elements which accept child nodes.
8976          * @param {String} msg (optional) A message to display in the mask
8977          * @param {String} msgCls (optional) A css class to apply to the msg element
8978          * @return {Element} The mask  element
8979          */
8980         mask : function(msg, msgCls)
8981         {
8982             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8983                 this.setStyle("position", "relative");
8984             }
8985             if(!this._mask){
8986                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8987             }
8988             this.addClass("x-masked");
8989             this._mask.setDisplayed(true);
8990             
8991             // we wander
8992             var z = 0;
8993             var dom = this.dom
8994             while (dom && dom.style) {
8995                 if (!isNaN(parseInt(dom.style.zIndex))) {
8996                     z = Math.max(z, parseInt(dom.style.zIndex));
8997                 }
8998                 dom = dom.parentNode;
8999             }
9000             // if we are masking the body - then it hides everything..
9001             if (this.dom == document.body) {
9002                 z = 1000000;
9003                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9004                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9005             }
9006            
9007             if(typeof msg == 'string'){
9008                 if(!this._maskMsg){
9009                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9010                 }
9011                 var mm = this._maskMsg;
9012                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9013                 if (mm.dom.firstChild) { // weird IE issue?
9014                     mm.dom.firstChild.innerHTML = msg;
9015                 }
9016                 mm.setDisplayed(true);
9017                 mm.center(this);
9018                 mm.setStyle('z-index', z + 102);
9019             }
9020             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9021                 this._mask.setHeight(this.getHeight());
9022             }
9023             this._mask.setStyle('z-index', z + 100);
9024             
9025             return this._mask;
9026         },
9027
9028         /**
9029          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9030          * it is cached for reuse.
9031          */
9032         unmask : function(removeEl){
9033             if(this._mask){
9034                 if(removeEl === true){
9035                     this._mask.remove();
9036                     delete this._mask;
9037                     if(this._maskMsg){
9038                         this._maskMsg.remove();
9039                         delete this._maskMsg;
9040                     }
9041                 }else{
9042                     this._mask.setDisplayed(false);
9043                     if(this._maskMsg){
9044                         this._maskMsg.setDisplayed(false);
9045                     }
9046                 }
9047             }
9048             this.removeClass("x-masked");
9049         },
9050
9051         /**
9052          * Returns true if this element is masked
9053          * @return {Boolean}
9054          */
9055         isMasked : function(){
9056             return this._mask && this._mask.isVisible();
9057         },
9058
9059         /**
9060          * Creates an iframe shim for this element to keep selects and other windowed objects from
9061          * showing through.
9062          * @return {Roo.Element} The new shim element
9063          */
9064         createShim : function(){
9065             var el = document.createElement('iframe');
9066             el.frameBorder = 'no';
9067             el.className = 'roo-shim';
9068             if(Roo.isIE && Roo.isSecure){
9069                 el.src = Roo.SSL_SECURE_URL;
9070             }
9071             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9072             shim.autoBoxAdjust = false;
9073             return shim;
9074         },
9075
9076         /**
9077          * Removes this element from the DOM and deletes it from the cache
9078          */
9079         remove : function(){
9080             if(this.dom.parentNode){
9081                 this.dom.parentNode.removeChild(this.dom);
9082             }
9083             delete El.cache[this.dom.id];
9084         },
9085
9086         /**
9087          * Sets up event handlers to add and remove a css class when the mouse is over this element
9088          * @param {String} className
9089          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9090          * mouseout events for children elements
9091          * @return {Roo.Element} this
9092          */
9093         addClassOnOver : function(className, preventFlicker){
9094             this.on("mouseover", function(){
9095                 Roo.fly(this, '_internal').addClass(className);
9096             }, this.dom);
9097             var removeFn = function(e){
9098                 if(preventFlicker !== true || !e.within(this, true)){
9099                     Roo.fly(this, '_internal').removeClass(className);
9100                 }
9101             };
9102             this.on("mouseout", removeFn, this.dom);
9103             return this;
9104         },
9105
9106         /**
9107          * Sets up event handlers to add and remove a css class when this element has the focus
9108          * @param {String} className
9109          * @return {Roo.Element} this
9110          */
9111         addClassOnFocus : function(className){
9112             this.on("focus", function(){
9113                 Roo.fly(this, '_internal').addClass(className);
9114             }, this.dom);
9115             this.on("blur", function(){
9116                 Roo.fly(this, '_internal').removeClass(className);
9117             }, this.dom);
9118             return this;
9119         },
9120         /**
9121          * 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)
9122          * @param {String} className
9123          * @return {Roo.Element} this
9124          */
9125         addClassOnClick : function(className){
9126             var dom = this.dom;
9127             this.on("mousedown", function(){
9128                 Roo.fly(dom, '_internal').addClass(className);
9129                 var d = Roo.get(document);
9130                 var fn = function(){
9131                     Roo.fly(dom, '_internal').removeClass(className);
9132                     d.removeListener("mouseup", fn);
9133                 };
9134                 d.on("mouseup", fn);
9135             });
9136             return this;
9137         },
9138
9139         /**
9140          * Stops the specified event from bubbling and optionally prevents the default action
9141          * @param {String} eventName
9142          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9143          * @return {Roo.Element} this
9144          */
9145         swallowEvent : function(eventName, preventDefault){
9146             var fn = function(e){
9147                 e.stopPropagation();
9148                 if(preventDefault){
9149                     e.preventDefault();
9150                 }
9151             };
9152             if(eventName instanceof Array){
9153                 for(var i = 0, len = eventName.length; i < len; i++){
9154                      this.on(eventName[i], fn);
9155                 }
9156                 return this;
9157             }
9158             this.on(eventName, fn);
9159             return this;
9160         },
9161
9162         /**
9163          * @private
9164          */
9165       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9166
9167         /**
9168          * Sizes this element to its parent element's dimensions performing
9169          * neccessary box adjustments.
9170          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9171          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9172          * @return {Roo.Element} this
9173          */
9174         fitToParent : function(monitorResize, targetParent) {
9175           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9176           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9177           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9178             return;
9179           }
9180           var p = Roo.get(targetParent || this.dom.parentNode);
9181           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9182           if (monitorResize === true) {
9183             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9184             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9185           }
9186           return this;
9187         },
9188
9189         /**
9190          * Gets the next sibling, skipping text nodes
9191          * @return {HTMLElement} The next sibling or null
9192          */
9193         getNextSibling : function(){
9194             var n = this.dom.nextSibling;
9195             while(n && n.nodeType != 1){
9196                 n = n.nextSibling;
9197             }
9198             return n;
9199         },
9200
9201         /**
9202          * Gets the previous sibling, skipping text nodes
9203          * @return {HTMLElement} The previous sibling or null
9204          */
9205         getPrevSibling : function(){
9206             var n = this.dom.previousSibling;
9207             while(n && n.nodeType != 1){
9208                 n = n.previousSibling;
9209             }
9210             return n;
9211         },
9212
9213
9214         /**
9215          * Appends the passed element(s) to this element
9216          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9217          * @return {Roo.Element} this
9218          */
9219         appendChild: function(el){
9220             el = Roo.get(el);
9221             el.appendTo(this);
9222             return this;
9223         },
9224
9225         /**
9226          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9227          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9228          * automatically generated with the specified attributes.
9229          * @param {HTMLElement} insertBefore (optional) a child element of this element
9230          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9231          * @return {Roo.Element} The new child element
9232          */
9233         createChild: function(config, insertBefore, returnDom){
9234             config = config || {tag:'div'};
9235             if(insertBefore){
9236                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9237             }
9238             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9239         },
9240
9241         /**
9242          * Appends this element to the passed element
9243          * @param {String/HTMLElement/Element} el The new parent element
9244          * @return {Roo.Element} this
9245          */
9246         appendTo: function(el){
9247             el = Roo.getDom(el);
9248             el.appendChild(this.dom);
9249             return this;
9250         },
9251
9252         /**
9253          * Inserts this element before the passed element in the DOM
9254          * @param {String/HTMLElement/Element} el The element to insert before
9255          * @return {Roo.Element} this
9256          */
9257         insertBefore: function(el){
9258             el = Roo.getDom(el);
9259             el.parentNode.insertBefore(this.dom, el);
9260             return this;
9261         },
9262
9263         /**
9264          * Inserts this element after the passed element in the DOM
9265          * @param {String/HTMLElement/Element} el The element to insert after
9266          * @return {Roo.Element} this
9267          */
9268         insertAfter: function(el){
9269             el = Roo.getDom(el);
9270             el.parentNode.insertBefore(this.dom, el.nextSibling);
9271             return this;
9272         },
9273
9274         /**
9275          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9276          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9277          * @return {Roo.Element} The new child
9278          */
9279         insertFirst: function(el, returnDom){
9280             el = el || {};
9281             if(typeof el == 'object' && !el.nodeType){ // dh config
9282                 return this.createChild(el, this.dom.firstChild, returnDom);
9283             }else{
9284                 el = Roo.getDom(el);
9285                 this.dom.insertBefore(el, this.dom.firstChild);
9286                 return !returnDom ? Roo.get(el) : el;
9287             }
9288         },
9289
9290         /**
9291          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9292          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9293          * @param {String} where (optional) 'before' or 'after' defaults to before
9294          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9295          * @return {Roo.Element} the inserted Element
9296          */
9297         insertSibling: function(el, where, returnDom){
9298             where = where ? where.toLowerCase() : 'before';
9299             el = el || {};
9300             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9301
9302             if(typeof el == 'object' && !el.nodeType){ // dh config
9303                 if(where == 'after' && !this.dom.nextSibling){
9304                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9305                 }else{
9306                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9307                 }
9308
9309             }else{
9310                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9311                             where == 'before' ? this.dom : this.dom.nextSibling);
9312                 if(!returnDom){
9313                     rt = Roo.get(rt);
9314                 }
9315             }
9316             return rt;
9317         },
9318
9319         /**
9320          * Creates and wraps this element with another element
9321          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9322          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9323          * @return {HTMLElement/Element} The newly created wrapper element
9324          */
9325         wrap: function(config, returnDom){
9326             if(!config){
9327                 config = {tag: "div"};
9328             }
9329             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9330             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9331             return newEl;
9332         },
9333
9334         /**
9335          * Replaces the passed element with this element
9336          * @param {String/HTMLElement/Element} el The element to replace
9337          * @return {Roo.Element} this
9338          */
9339         replace: function(el){
9340             el = Roo.get(el);
9341             this.insertBefore(el);
9342             el.remove();
9343             return this;
9344         },
9345
9346         /**
9347          * Inserts an html fragment into this element
9348          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9349          * @param {String} html The HTML fragment
9350          * @param {Boolean} returnEl True to return an Roo.Element
9351          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9352          */
9353         insertHtml : function(where, html, returnEl){
9354             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9355             return returnEl ? Roo.get(el) : el;
9356         },
9357
9358         /**
9359          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9360          * @param {Object} o The object with the attributes
9361          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9362          * @return {Roo.Element} this
9363          */
9364         set : function(o, useSet){
9365             var el = this.dom;
9366             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9367             for(var attr in o){
9368                 if(attr == "style" || typeof o[attr] == "function") continue;
9369                 if(attr=="cls"){
9370                     el.className = o["cls"];
9371                 }else{
9372                     if(useSet) el.setAttribute(attr, o[attr]);
9373                     else el[attr] = o[attr];
9374                 }
9375             }
9376             if(o.style){
9377                 Roo.DomHelper.applyStyles(el, o.style);
9378             }
9379             return this;
9380         },
9381
9382         /**
9383          * Convenience method for constructing a KeyMap
9384          * @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:
9385          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9386          * @param {Function} fn The function to call
9387          * @param {Object} scope (optional) The scope of the function
9388          * @return {Roo.KeyMap} The KeyMap created
9389          */
9390         addKeyListener : function(key, fn, scope){
9391             var config;
9392             if(typeof key != "object" || key instanceof Array){
9393                 config = {
9394                     key: key,
9395                     fn: fn,
9396                     scope: scope
9397                 };
9398             }else{
9399                 config = {
9400                     key : key.key,
9401                     shift : key.shift,
9402                     ctrl : key.ctrl,
9403                     alt : key.alt,
9404                     fn: fn,
9405                     scope: scope
9406                 };
9407             }
9408             return new Roo.KeyMap(this, config);
9409         },
9410
9411         /**
9412          * Creates a KeyMap for this element
9413          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9414          * @return {Roo.KeyMap} The KeyMap created
9415          */
9416         addKeyMap : function(config){
9417             return new Roo.KeyMap(this, config);
9418         },
9419
9420         /**
9421          * Returns true if this element is scrollable.
9422          * @return {Boolean}
9423          */
9424          isScrollable : function(){
9425             var dom = this.dom;
9426             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9427         },
9428
9429         /**
9430          * 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().
9431          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9432          * @param {Number} value The new scroll value
9433          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9434          * @return {Element} this
9435          */
9436
9437         scrollTo : function(side, value, animate){
9438             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9439             if(!animate || !A){
9440                 this.dom[prop] = value;
9441             }else{
9442                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9443                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9444             }
9445             return this;
9446         },
9447
9448         /**
9449          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9450          * within this element's scrollable range.
9451          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9452          * @param {Number} distance How far to scroll the element in pixels
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9455          * was scrolled as far as it could go.
9456          */
9457          scroll : function(direction, distance, animate){
9458              if(!this.isScrollable()){
9459                  return;
9460              }
9461              var el = this.dom;
9462              var l = el.scrollLeft, t = el.scrollTop;
9463              var w = el.scrollWidth, h = el.scrollHeight;
9464              var cw = el.clientWidth, ch = el.clientHeight;
9465              direction = direction.toLowerCase();
9466              var scrolled = false;
9467              var a = this.preanim(arguments, 2);
9468              switch(direction){
9469                  case "l":
9470                  case "left":
9471                      if(w - l > cw){
9472                          var v = Math.min(l + distance, w-cw);
9473                          this.scrollTo("left", v, a);
9474                          scrolled = true;
9475                      }
9476                      break;
9477                 case "r":
9478                 case "right":
9479                      if(l > 0){
9480                          var v = Math.max(l - distance, 0);
9481                          this.scrollTo("left", v, a);
9482                          scrolled = true;
9483                      }
9484                      break;
9485                 case "t":
9486                 case "top":
9487                 case "up":
9488                      if(t > 0){
9489                          var v = Math.max(t - distance, 0);
9490                          this.scrollTo("top", v, a);
9491                          scrolled = true;
9492                      }
9493                      break;
9494                 case "b":
9495                 case "bottom":
9496                 case "down":
9497                      if(h - t > ch){
9498                          var v = Math.min(t + distance, h-ch);
9499                          this.scrollTo("top", v, a);
9500                          scrolled = true;
9501                      }
9502                      break;
9503              }
9504              return scrolled;
9505         },
9506
9507         /**
9508          * Translates the passed page coordinates into left/top css values for this element
9509          * @param {Number/Array} x The page x or an array containing [x, y]
9510          * @param {Number} y The page y
9511          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9512          */
9513         translatePoints : function(x, y){
9514             if(typeof x == 'object' || x instanceof Array){
9515                 y = x[1]; x = x[0];
9516             }
9517             var p = this.getStyle('position');
9518             var o = this.getXY();
9519
9520             var l = parseInt(this.getStyle('left'), 10);
9521             var t = parseInt(this.getStyle('top'), 10);
9522
9523             if(isNaN(l)){
9524                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9525             }
9526             if(isNaN(t)){
9527                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9528             }
9529
9530             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9531         },
9532
9533         /**
9534          * Returns the current scroll position of the element.
9535          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9536          */
9537         getScroll : function(){
9538             var d = this.dom, doc = document;
9539             if(d == doc || d == doc.body){
9540                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9541                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9542                 return {left: l, top: t};
9543             }else{
9544                 return {left: d.scrollLeft, top: d.scrollTop};
9545             }
9546         },
9547
9548         /**
9549          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9550          * are convert to standard 6 digit hex color.
9551          * @param {String} attr The css attribute
9552          * @param {String} defaultValue The default value to use when a valid color isn't found
9553          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9554          * YUI color anims.
9555          */
9556         getColor : function(attr, defaultValue, prefix){
9557             var v = this.getStyle(attr);
9558             if(!v || v == "transparent" || v == "inherit") {
9559                 return defaultValue;
9560             }
9561             var color = typeof prefix == "undefined" ? "#" : prefix;
9562             if(v.substr(0, 4) == "rgb("){
9563                 var rvs = v.slice(4, v.length -1).split(",");
9564                 for(var i = 0; i < 3; i++){
9565                     var h = parseInt(rvs[i]).toString(16);
9566                     if(h < 16){
9567                         h = "0" + h;
9568                     }
9569                     color += h;
9570                 }
9571             } else {
9572                 if(v.substr(0, 1) == "#"){
9573                     if(v.length == 4) {
9574                         for(var i = 1; i < 4; i++){
9575                             var c = v.charAt(i);
9576                             color +=  c + c;
9577                         }
9578                     }else if(v.length == 7){
9579                         color += v.substr(1);
9580                     }
9581                 }
9582             }
9583             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9584         },
9585
9586         /**
9587          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9588          * gradient background, rounded corners and a 4-way shadow.
9589          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9590          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9591          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9592          * @return {Roo.Element} this
9593          */
9594         boxWrap : function(cls){
9595             cls = cls || 'x-box';
9596             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9597             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9598             return el;
9599         },
9600
9601         /**
9602          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9603          * @param {String} namespace The namespace in which to look for the attribute
9604          * @param {String} name The attribute name
9605          * @return {String} The attribute value
9606          */
9607         getAttributeNS : Roo.isIE ? function(ns, name){
9608             var d = this.dom;
9609             var type = typeof d[ns+":"+name];
9610             if(type != 'undefined' && type != 'unknown'){
9611                 return d[ns+":"+name];
9612             }
9613             return d[name];
9614         } : function(ns, name){
9615             var d = this.dom;
9616             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9617         },
9618         
9619         
9620         /**
9621          * Sets or Returns the value the dom attribute value
9622          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9623          * @param {String} value (optional) The value to set the attribute to
9624          * @return {String} The attribute value
9625          */
9626         attr : function(name){
9627             if (arguments.length > 1) {
9628                 this.dom.setAttribute(name, arguments[1]);
9629                 return arguments[1];
9630             }
9631             if (typeof(name) == 'object') {
9632                 for(var i in name) {
9633                     this.attr(i, name[i]);
9634                 }
9635                 return name;
9636             }
9637             
9638             
9639             if (!this.dom.hasAttribute(name)) {
9640                 return undefined;
9641             }
9642             return this.dom.getAttribute(name);
9643         }
9644         
9645         
9646         
9647     };
9648
9649     var ep = El.prototype;
9650
9651     /**
9652      * Appends an event handler (Shorthand for addListener)
9653      * @param {String}   eventName     The type of event to append
9654      * @param {Function} fn        The method the event invokes
9655      * @param {Object} scope       (optional) The scope (this object) of the fn
9656      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9657      * @method
9658      */
9659     ep.on = ep.addListener;
9660         // backwards compat
9661     ep.mon = ep.addListener;
9662
9663     /**
9664      * Removes an event handler from this element (shorthand for removeListener)
9665      * @param {String} eventName the type of event to remove
9666      * @param {Function} fn the method the event invokes
9667      * @return {Roo.Element} this
9668      * @method
9669      */
9670     ep.un = ep.removeListener;
9671
9672     /**
9673      * true to automatically adjust width and height settings for box-model issues (default to true)
9674      */
9675     ep.autoBoxAdjust = true;
9676
9677     // private
9678     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9679
9680     // private
9681     El.addUnits = function(v, defaultUnit){
9682         if(v === "" || v == "auto"){
9683             return v;
9684         }
9685         if(v === undefined){
9686             return '';
9687         }
9688         if(typeof v == "number" || !El.unitPattern.test(v)){
9689             return v + (defaultUnit || 'px');
9690         }
9691         return v;
9692     };
9693
9694     // special markup used throughout Roo when box wrapping elements
9695     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>';
9696     /**
9697      * Visibility mode constant - Use visibility to hide element
9698      * @static
9699      * @type Number
9700      */
9701     El.VISIBILITY = 1;
9702     /**
9703      * Visibility mode constant - Use display to hide element
9704      * @static
9705      * @type Number
9706      */
9707     El.DISPLAY = 2;
9708
9709     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9710     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9711     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9712
9713
9714
9715     /**
9716      * @private
9717      */
9718     El.cache = {};
9719
9720     var docEl;
9721
9722     /**
9723      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9724      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9725      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9726      * @return {Element} The Element object
9727      * @static
9728      */
9729     El.get = function(el){
9730         var ex, elm, id;
9731         if(!el){ return null; }
9732         if(typeof el == "string"){ // element id
9733             if(!(elm = document.getElementById(el))){
9734                 return null;
9735             }
9736             if(ex = El.cache[el]){
9737                 ex.dom = elm;
9738             }else{
9739                 ex = El.cache[el] = new El(elm);
9740             }
9741             return ex;
9742         }else if(el.tagName){ // dom element
9743             if(!(id = el.id)){
9744                 id = Roo.id(el);
9745             }
9746             if(ex = El.cache[id]){
9747                 ex.dom = el;
9748             }else{
9749                 ex = El.cache[id] = new El(el);
9750             }
9751             return ex;
9752         }else if(el instanceof El){
9753             if(el != docEl){
9754                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9755                                                               // catch case where it hasn't been appended
9756                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9757             }
9758             return el;
9759         }else if(el.isComposite){
9760             return el;
9761         }else if(el instanceof Array){
9762             return El.select(el);
9763         }else if(el == document){
9764             // create a bogus element object representing the document object
9765             if(!docEl){
9766                 var f = function(){};
9767                 f.prototype = El.prototype;
9768                 docEl = new f();
9769                 docEl.dom = document;
9770             }
9771             return docEl;
9772         }
9773         return null;
9774     };
9775
9776     // private
9777     El.uncache = function(el){
9778         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9779             if(a[i]){
9780                 delete El.cache[a[i].id || a[i]];
9781             }
9782         }
9783     };
9784
9785     // private
9786     // Garbage collection - uncache elements/purge listeners on orphaned elements
9787     // so we don't hold a reference and cause the browser to retain them
9788     El.garbageCollect = function(){
9789         if(!Roo.enableGarbageCollector){
9790             clearInterval(El.collectorThread);
9791             return;
9792         }
9793         for(var eid in El.cache){
9794             var el = El.cache[eid], d = el.dom;
9795             // -------------------------------------------------------
9796             // Determining what is garbage:
9797             // -------------------------------------------------------
9798             // !d
9799             // dom node is null, definitely garbage
9800             // -------------------------------------------------------
9801             // !d.parentNode
9802             // no parentNode == direct orphan, definitely garbage
9803             // -------------------------------------------------------
9804             // !d.offsetParent && !document.getElementById(eid)
9805             // display none elements have no offsetParent so we will
9806             // also try to look it up by it's id. However, check
9807             // offsetParent first so we don't do unneeded lookups.
9808             // This enables collection of elements that are not orphans
9809             // directly, but somewhere up the line they have an orphan
9810             // parent.
9811             // -------------------------------------------------------
9812             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9813                 delete El.cache[eid];
9814                 if(d && Roo.enableListenerCollection){
9815                     E.purgeElement(d);
9816                 }
9817             }
9818         }
9819     }
9820     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9821
9822
9823     // dom is optional
9824     El.Flyweight = function(dom){
9825         this.dom = dom;
9826     };
9827     El.Flyweight.prototype = El.prototype;
9828
9829     El._flyweights = {};
9830     /**
9831      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9832      * the dom node can be overwritten by other code.
9833      * @param {String/HTMLElement} el The dom node or id
9834      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9835      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9836      * @static
9837      * @return {Element} The shared Element object
9838      */
9839     El.fly = function(el, named){
9840         named = named || '_global';
9841         el = Roo.getDom(el);
9842         if(!el){
9843             return null;
9844         }
9845         if(!El._flyweights[named]){
9846             El._flyweights[named] = new El.Flyweight();
9847         }
9848         El._flyweights[named].dom = el;
9849         return El._flyweights[named];
9850     };
9851
9852     /**
9853      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9854      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9855      * Shorthand of {@link Roo.Element#get}
9856      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9857      * @return {Element} The Element object
9858      * @member Roo
9859      * @method get
9860      */
9861     Roo.get = El.get;
9862     /**
9863      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9864      * the dom node can be overwritten by other code.
9865      * Shorthand of {@link Roo.Element#fly}
9866      * @param {String/HTMLElement} el The dom node or id
9867      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9868      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9869      * @static
9870      * @return {Element} The shared Element object
9871      * @member Roo
9872      * @method fly
9873      */
9874     Roo.fly = El.fly;
9875
9876     // speedy lookup for elements never to box adjust
9877     var noBoxAdjust = Roo.isStrict ? {
9878         select:1
9879     } : {
9880         input:1, select:1, textarea:1
9881     };
9882     if(Roo.isIE || Roo.isGecko){
9883         noBoxAdjust['button'] = 1;
9884     }
9885
9886
9887     Roo.EventManager.on(window, 'unload', function(){
9888         delete El.cache;
9889         delete El._flyweights;
9890     });
9891 })();
9892
9893
9894
9895
9896 if(Roo.DomQuery){
9897     Roo.Element.selectorFunction = Roo.DomQuery.select;
9898 }
9899
9900 Roo.Element.select = function(selector, unique, root){
9901     var els;
9902     if(typeof selector == "string"){
9903         els = Roo.Element.selectorFunction(selector, root);
9904     }else if(selector.length !== undefined){
9905         els = selector;
9906     }else{
9907         throw "Invalid selector";
9908     }
9909     if(unique === true){
9910         return new Roo.CompositeElement(els);
9911     }else{
9912         return new Roo.CompositeElementLite(els);
9913     }
9914 };
9915 /**
9916  * Selects elements based on the passed CSS selector to enable working on them as 1.
9917  * @param {String/Array} selector The CSS selector or an array of elements
9918  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9919  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9920  * @return {CompositeElementLite/CompositeElement}
9921  * @member Roo
9922  * @method select
9923  */
9924 Roo.select = Roo.Element.select;
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939 /*
9940  * Based on:
9941  * Ext JS Library 1.1.1
9942  * Copyright(c) 2006-2007, Ext JS, LLC.
9943  *
9944  * Originally Released Under LGPL - original licence link has changed is not relivant.
9945  *
9946  * Fork - LGPL
9947  * <script type="text/javascript">
9948  */
9949
9950
9951
9952 //Notifies Element that fx methods are available
9953 Roo.enableFx = true;
9954
9955 /**
9956  * @class Roo.Fx
9957  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9958  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9959  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9960  * Element effects to work.</p><br/>
9961  *
9962  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9963  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9964  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9965  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9966  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9967  * expected results and should be done with care.</p><br/>
9968  *
9969  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9970  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9971 <pre>
9972 Value  Description
9973 -----  -----------------------------
9974 tl     The top left corner
9975 t      The center of the top edge
9976 tr     The top right corner
9977 l      The center of the left edge
9978 r      The center of the right edge
9979 bl     The bottom left corner
9980 b      The center of the bottom edge
9981 br     The bottom right corner
9982 </pre>
9983  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9984  * below are common options that can be passed to any Fx method.</b>
9985  * @cfg {Function} callback A function called when the effect is finished
9986  * @cfg {Object} scope The scope of the effect function
9987  * @cfg {String} easing A valid Easing value for the effect
9988  * @cfg {String} afterCls A css class to apply after the effect
9989  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9990  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9991  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9992  * effects that end with the element being visually hidden, ignored otherwise)
9993  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9994  * a function which returns such a specification that will be applied to the Element after the effect finishes
9995  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9996  * @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
9997  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9998  */
9999 Roo.Fx = {
10000         /**
10001          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10002          * origin for the slide effect.  This function automatically handles wrapping the element with
10003          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10004          * Usage:
10005          *<pre><code>
10006 // default: slide the element in from the top
10007 el.slideIn();
10008
10009 // custom: slide the element in from the right with a 2-second duration
10010 el.slideIn('r', { duration: 2 });
10011
10012 // common config options shown with default values
10013 el.slideIn('t', {
10014     easing: 'easeOut',
10015     duration: .5
10016 });
10017 </code></pre>
10018          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10019          * @param {Object} options (optional) Object literal with any of the Fx config options
10020          * @return {Roo.Element} The Element
10021          */
10022     slideIn : function(anchor, o){
10023         var el = this.getFxEl();
10024         o = o || {};
10025
10026         el.queueFx(o, function(){
10027
10028             anchor = anchor || "t";
10029
10030             // fix display to visibility
10031             this.fixDisplay();
10032
10033             // restore values after effect
10034             var r = this.getFxRestore();
10035             var b = this.getBox();
10036             // fixed size for slide
10037             this.setSize(b);
10038
10039             // wrap if needed
10040             var wrap = this.fxWrap(r.pos, o, "hidden");
10041
10042             var st = this.dom.style;
10043             st.visibility = "visible";
10044             st.position = "absolute";
10045
10046             // clear out temp styles after slide and unwrap
10047             var after = function(){
10048                 el.fxUnwrap(wrap, r.pos, o);
10049                 st.width = r.width;
10050                 st.height = r.height;
10051                 el.afterFx(o);
10052             };
10053             // time to calc the positions
10054             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10055
10056             switch(anchor.toLowerCase()){
10057                 case "t":
10058                     wrap.setSize(b.width, 0);
10059                     st.left = st.bottom = "0";
10060                     a = {height: bh};
10061                 break;
10062                 case "l":
10063                     wrap.setSize(0, b.height);
10064                     st.right = st.top = "0";
10065                     a = {width: bw};
10066                 break;
10067                 case "r":
10068                     wrap.setSize(0, b.height);
10069                     wrap.setX(b.right);
10070                     st.left = st.top = "0";
10071                     a = {width: bw, points: pt};
10072                 break;
10073                 case "b":
10074                     wrap.setSize(b.width, 0);
10075                     wrap.setY(b.bottom);
10076                     st.left = st.top = "0";
10077                     a = {height: bh, points: pt};
10078                 break;
10079                 case "tl":
10080                     wrap.setSize(0, 0);
10081                     st.right = st.bottom = "0";
10082                     a = {width: bw, height: bh};
10083                 break;
10084                 case "bl":
10085                     wrap.setSize(0, 0);
10086                     wrap.setY(b.y+b.height);
10087                     st.right = st.top = "0";
10088                     a = {width: bw, height: bh, points: pt};
10089                 break;
10090                 case "br":
10091                     wrap.setSize(0, 0);
10092                     wrap.setXY([b.right, b.bottom]);
10093                     st.left = st.top = "0";
10094                     a = {width: bw, height: bh, points: pt};
10095                 break;
10096                 case "tr":
10097                     wrap.setSize(0, 0);
10098                     wrap.setX(b.x+b.width);
10099                     st.left = st.bottom = "0";
10100                     a = {width: bw, height: bh, points: pt};
10101                 break;
10102             }
10103             this.dom.style.visibility = "visible";
10104             wrap.show();
10105
10106             arguments.callee.anim = wrap.fxanim(a,
10107                 o,
10108                 'motion',
10109                 .5,
10110                 'easeOut', after);
10111         });
10112         return this;
10113     },
10114     
10115         /**
10116          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10117          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10118          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10119          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10120          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10121          * Usage:
10122          *<pre><code>
10123 // default: slide the element out to the top
10124 el.slideOut();
10125
10126 // custom: slide the element out to the right with a 2-second duration
10127 el.slideOut('r', { duration: 2 });
10128
10129 // common config options shown with default values
10130 el.slideOut('t', {
10131     easing: 'easeOut',
10132     duration: .5,
10133     remove: false,
10134     useDisplay: false
10135 });
10136 </code></pre>
10137          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10138          * @param {Object} options (optional) Object literal with any of the Fx config options
10139          * @return {Roo.Element} The Element
10140          */
10141     slideOut : function(anchor, o){
10142         var el = this.getFxEl();
10143         o = o || {};
10144
10145         el.queueFx(o, function(){
10146
10147             anchor = anchor || "t";
10148
10149             // restore values after effect
10150             var r = this.getFxRestore();
10151             
10152             var b = this.getBox();
10153             // fixed size for slide
10154             this.setSize(b);
10155
10156             // wrap if needed
10157             var wrap = this.fxWrap(r.pos, o, "visible");
10158
10159             var st = this.dom.style;
10160             st.visibility = "visible";
10161             st.position = "absolute";
10162
10163             wrap.setSize(b);
10164
10165             var after = function(){
10166                 if(o.useDisplay){
10167                     el.setDisplayed(false);
10168                 }else{
10169                     el.hide();
10170                 }
10171
10172                 el.fxUnwrap(wrap, r.pos, o);
10173
10174                 st.width = r.width;
10175                 st.height = r.height;
10176
10177                 el.afterFx(o);
10178             };
10179
10180             var a, zero = {to: 0};
10181             switch(anchor.toLowerCase()){
10182                 case "t":
10183                     st.left = st.bottom = "0";
10184                     a = {height: zero};
10185                 break;
10186                 case "l":
10187                     st.right = st.top = "0";
10188                     a = {width: zero};
10189                 break;
10190                 case "r":
10191                     st.left = st.top = "0";
10192                     a = {width: zero, points: {to:[b.right, b.y]}};
10193                 break;
10194                 case "b":
10195                     st.left = st.top = "0";
10196                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10197                 break;
10198                 case "tl":
10199                     st.right = st.bottom = "0";
10200                     a = {width: zero, height: zero};
10201                 break;
10202                 case "bl":
10203                     st.right = st.top = "0";
10204                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10205                 break;
10206                 case "br":
10207                     st.left = st.top = "0";
10208                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10209                 break;
10210                 case "tr":
10211                     st.left = st.bottom = "0";
10212                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10213                 break;
10214             }
10215
10216             arguments.callee.anim = wrap.fxanim(a,
10217                 o,
10218                 'motion',
10219                 .5,
10220                 "easeOut", after);
10221         });
10222         return this;
10223     },
10224
10225         /**
10226          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10227          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10228          * The element must be removed from the DOM using the 'remove' config option if desired.
10229          * Usage:
10230          *<pre><code>
10231 // default
10232 el.puff();
10233
10234 // common config options shown with default values
10235 el.puff({
10236     easing: 'easeOut',
10237     duration: .5,
10238     remove: false,
10239     useDisplay: false
10240 });
10241 </code></pre>
10242          * @param {Object} options (optional) Object literal with any of the Fx config options
10243          * @return {Roo.Element} The Element
10244          */
10245     puff : function(o){
10246         var el = this.getFxEl();
10247         o = o || {};
10248
10249         el.queueFx(o, function(){
10250             this.clearOpacity();
10251             this.show();
10252
10253             // restore values after effect
10254             var r = this.getFxRestore();
10255             var st = this.dom.style;
10256
10257             var after = function(){
10258                 if(o.useDisplay){
10259                     el.setDisplayed(false);
10260                 }else{
10261                     el.hide();
10262                 }
10263
10264                 el.clearOpacity();
10265
10266                 el.setPositioning(r.pos);
10267                 st.width = r.width;
10268                 st.height = r.height;
10269                 st.fontSize = '';
10270                 el.afterFx(o);
10271             };
10272
10273             var width = this.getWidth();
10274             var height = this.getHeight();
10275
10276             arguments.callee.anim = this.fxanim({
10277                     width : {to: this.adjustWidth(width * 2)},
10278                     height : {to: this.adjustHeight(height * 2)},
10279                     points : {by: [-(width * .5), -(height * .5)]},
10280                     opacity : {to: 0},
10281                     fontSize: {to:200, unit: "%"}
10282                 },
10283                 o,
10284                 'motion',
10285                 .5,
10286                 "easeOut", after);
10287         });
10288         return this;
10289     },
10290
10291         /**
10292          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10293          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10294          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10295          * Usage:
10296          *<pre><code>
10297 // default
10298 el.switchOff();
10299
10300 // all config options shown with default values
10301 el.switchOff({
10302     easing: 'easeIn',
10303     duration: .3,
10304     remove: false,
10305     useDisplay: false
10306 });
10307 </code></pre>
10308          * @param {Object} options (optional) Object literal with any of the Fx config options
10309          * @return {Roo.Element} The Element
10310          */
10311     switchOff : function(o){
10312         var el = this.getFxEl();
10313         o = o || {};
10314
10315         el.queueFx(o, function(){
10316             this.clearOpacity();
10317             this.clip();
10318
10319             // restore values after effect
10320             var r = this.getFxRestore();
10321             var st = this.dom.style;
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.clearOpacity();
10331                 el.setPositioning(r.pos);
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10339                 this.clearOpacity();
10340                 (function(){
10341                     this.fxanim({
10342                         height:{to:1},
10343                         points:{by:[0, this.getHeight() * .5]}
10344                     }, o, 'motion', 0.3, 'easeIn', after);
10345                 }).defer(100, this);
10346             });
10347         });
10348         return this;
10349     },
10350
10351     /**
10352      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10353      * changed using the "attr" config option) and then fading back to the original color. If no original
10354      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10355      * Usage:
10356 <pre><code>
10357 // default: highlight background to yellow
10358 el.highlight();
10359
10360 // custom: highlight foreground text to blue for 2 seconds
10361 el.highlight("0000ff", { attr: 'color', duration: 2 });
10362
10363 // common config options shown with default values
10364 el.highlight("ffff9c", {
10365     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10366     endColor: (current color) or "ffffff",
10367     easing: 'easeIn',
10368     duration: 1
10369 });
10370 </code></pre>
10371      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10372      * @param {Object} options (optional) Object literal with any of the Fx config options
10373      * @return {Roo.Element} The Element
10374      */ 
10375     highlight : function(color, o){
10376         var el = this.getFxEl();
10377         o = o || {};
10378
10379         el.queueFx(o, function(){
10380             color = color || "ffff9c";
10381             attr = o.attr || "backgroundColor";
10382
10383             this.clearOpacity();
10384             this.show();
10385
10386             var origColor = this.getColor(attr);
10387             var restoreColor = this.dom.style[attr];
10388             endColor = (o.endColor || origColor) || "ffffff";
10389
10390             var after = function(){
10391                 el.dom.style[attr] = restoreColor;
10392                 el.afterFx(o);
10393             };
10394
10395             var a = {};
10396             a[attr] = {from: color, to: endColor};
10397             arguments.callee.anim = this.fxanim(a,
10398                 o,
10399                 'color',
10400                 1,
10401                 'easeIn', after);
10402         });
10403         return this;
10404     },
10405
10406    /**
10407     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10408     * Usage:
10409 <pre><code>
10410 // default: a single light blue ripple
10411 el.frame();
10412
10413 // custom: 3 red ripples lasting 3 seconds total
10414 el.frame("ff0000", 3, { duration: 3 });
10415
10416 // common config options shown with default values
10417 el.frame("C3DAF9", 1, {
10418     duration: 1 //duration of entire animation (not each individual ripple)
10419     // Note: Easing is not configurable and will be ignored if included
10420 });
10421 </code></pre>
10422     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10423     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10424     * @param {Object} options (optional) Object literal with any of the Fx config options
10425     * @return {Roo.Element} The Element
10426     */
10427     frame : function(color, count, o){
10428         var el = this.getFxEl();
10429         o = o || {};
10430
10431         el.queueFx(o, function(){
10432             color = color || "#C3DAF9";
10433             if(color.length == 6){
10434                 color = "#" + color;
10435             }
10436             count = count || 1;
10437             duration = o.duration || 1;
10438             this.show();
10439
10440             var b = this.getBox();
10441             var animFn = function(){
10442                 var proxy = this.createProxy({
10443
10444                      style:{
10445                         visbility:"hidden",
10446                         position:"absolute",
10447                         "z-index":"35000", // yee haw
10448                         border:"0px solid " + color
10449                      }
10450                   });
10451                 var scale = Roo.isBorderBox ? 2 : 1;
10452                 proxy.animate({
10453                     top:{from:b.y, to:b.y - 20},
10454                     left:{from:b.x, to:b.x - 20},
10455                     borderWidth:{from:0, to:10},
10456                     opacity:{from:1, to:0},
10457                     height:{from:b.height, to:(b.height + (20*scale))},
10458                     width:{from:b.width, to:(b.width + (20*scale))}
10459                 }, duration, function(){
10460                     proxy.remove();
10461                 });
10462                 if(--count > 0){
10463                      animFn.defer((duration/2)*1000, this);
10464                 }else{
10465                     el.afterFx(o);
10466                 }
10467             };
10468             animFn.call(this);
10469         });
10470         return this;
10471     },
10472
10473    /**
10474     * Creates a pause before any subsequent queued effects begin.  If there are
10475     * no effects queued after the pause it will have no effect.
10476     * Usage:
10477 <pre><code>
10478 el.pause(1);
10479 </code></pre>
10480     * @param {Number} seconds The length of time to pause (in seconds)
10481     * @return {Roo.Element} The Element
10482     */
10483     pause : function(seconds){
10484         var el = this.getFxEl();
10485         var o = {};
10486
10487         el.queueFx(o, function(){
10488             setTimeout(function(){
10489                 el.afterFx(o);
10490             }, seconds * 1000);
10491         });
10492         return this;
10493     },
10494
10495    /**
10496     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10497     * using the "endOpacity" config option.
10498     * Usage:
10499 <pre><code>
10500 // default: fade in from opacity 0 to 100%
10501 el.fadeIn();
10502
10503 // custom: fade in from opacity 0 to 75% over 2 seconds
10504 el.fadeIn({ endOpacity: .75, duration: 2});
10505
10506 // common config options shown with default values
10507 el.fadeIn({
10508     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10509     easing: 'easeOut',
10510     duration: .5
10511 });
10512 </code></pre>
10513     * @param {Object} options (optional) Object literal with any of the Fx config options
10514     * @return {Roo.Element} The Element
10515     */
10516     fadeIn : function(o){
10517         var el = this.getFxEl();
10518         o = o || {};
10519         el.queueFx(o, function(){
10520             this.setOpacity(0);
10521             this.fixDisplay();
10522             this.dom.style.visibility = 'visible';
10523             var to = o.endOpacity || 1;
10524             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10525                 o, null, .5, "easeOut", function(){
10526                 if(to == 1){
10527                     this.clearOpacity();
10528                 }
10529                 el.afterFx(o);
10530             });
10531         });
10532         return this;
10533     },
10534
10535    /**
10536     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10537     * using the "endOpacity" config option.
10538     * Usage:
10539 <pre><code>
10540 // default: fade out from the element's current opacity to 0
10541 el.fadeOut();
10542
10543 // custom: fade out from the element's current opacity to 25% over 2 seconds
10544 el.fadeOut({ endOpacity: .25, duration: 2});
10545
10546 // common config options shown with default values
10547 el.fadeOut({
10548     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10549     easing: 'easeOut',
10550     duration: .5
10551     remove: false,
10552     useDisplay: false
10553 });
10554 </code></pre>
10555     * @param {Object} options (optional) Object literal with any of the Fx config options
10556     * @return {Roo.Element} The Element
10557     */
10558     fadeOut : function(o){
10559         var el = this.getFxEl();
10560         o = o || {};
10561         el.queueFx(o, function(){
10562             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10563                 o, null, .5, "easeOut", function(){
10564                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10565                      this.dom.style.display = "none";
10566                 }else{
10567                      this.dom.style.visibility = "hidden";
10568                 }
10569                 this.clearOpacity();
10570                 el.afterFx(o);
10571             });
10572         });
10573         return this;
10574     },
10575
10576    /**
10577     * Animates the transition of an element's dimensions from a starting height/width
10578     * to an ending height/width.
10579     * Usage:
10580 <pre><code>
10581 // change height and width to 100x100 pixels
10582 el.scale(100, 100);
10583
10584 // common config options shown with default values.  The height and width will default to
10585 // the element's existing values if passed as null.
10586 el.scale(
10587     [element's width],
10588     [element's height], {
10589     easing: 'easeOut',
10590     duration: .35
10591 });
10592 </code></pre>
10593     * @param {Number} width  The new width (pass undefined to keep the original width)
10594     * @param {Number} height  The new height (pass undefined to keep the original height)
10595     * @param {Object} options (optional) Object literal with any of the Fx config options
10596     * @return {Roo.Element} The Element
10597     */
10598     scale : function(w, h, o){
10599         this.shift(Roo.apply({}, o, {
10600             width: w,
10601             height: h
10602         }));
10603         return this;
10604     },
10605
10606    /**
10607     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10608     * Any of these properties not specified in the config object will not be changed.  This effect 
10609     * requires that at least one new dimension, position or opacity setting must be passed in on
10610     * the config object in order for the function to have any effect.
10611     * Usage:
10612 <pre><code>
10613 // slide the element horizontally to x position 200 while changing the height and opacity
10614 el.shift({ x: 200, height: 50, opacity: .8 });
10615
10616 // common config options shown with default values.
10617 el.shift({
10618     width: [element's width],
10619     height: [element's height],
10620     x: [element's x position],
10621     y: [element's y position],
10622     opacity: [element's opacity],
10623     easing: 'easeOut',
10624     duration: .35
10625 });
10626 </code></pre>
10627     * @param {Object} options  Object literal with any of the Fx config options
10628     * @return {Roo.Element} The Element
10629     */
10630     shift : function(o){
10631         var el = this.getFxEl();
10632         o = o || {};
10633         el.queueFx(o, function(){
10634             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10635             if(w !== undefined){
10636                 a.width = {to: this.adjustWidth(w)};
10637             }
10638             if(h !== undefined){
10639                 a.height = {to: this.adjustHeight(h)};
10640             }
10641             if(x !== undefined || y !== undefined){
10642                 a.points = {to: [
10643                     x !== undefined ? x : this.getX(),
10644                     y !== undefined ? y : this.getY()
10645                 ]};
10646             }
10647             if(op !== undefined){
10648                 a.opacity = {to: op};
10649             }
10650             if(o.xy !== undefined){
10651                 a.points = {to: o.xy};
10652             }
10653             arguments.callee.anim = this.fxanim(a,
10654                 o, 'motion', .35, "easeOut", function(){
10655                 el.afterFx(o);
10656             });
10657         });
10658         return this;
10659     },
10660
10661         /**
10662          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10663          * ending point of the effect.
10664          * Usage:
10665          *<pre><code>
10666 // default: slide the element downward while fading out
10667 el.ghost();
10668
10669 // custom: slide the element out to the right with a 2-second duration
10670 el.ghost('r', { duration: 2 });
10671
10672 // common config options shown with default values
10673 el.ghost('b', {
10674     easing: 'easeOut',
10675     duration: .5
10676     remove: false,
10677     useDisplay: false
10678 });
10679 </code></pre>
10680          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10681          * @param {Object} options (optional) Object literal with any of the Fx config options
10682          * @return {Roo.Element} The Element
10683          */
10684     ghost : function(anchor, o){
10685         var el = this.getFxEl();
10686         o = o || {};
10687
10688         el.queueFx(o, function(){
10689             anchor = anchor || "b";
10690
10691             // restore values after effect
10692             var r = this.getFxRestore();
10693             var w = this.getWidth(),
10694                 h = this.getHeight();
10695
10696             var st = this.dom.style;
10697
10698             var after = function(){
10699                 if(o.useDisplay){
10700                     el.setDisplayed(false);
10701                 }else{
10702                     el.hide();
10703                 }
10704
10705                 el.clearOpacity();
10706                 el.setPositioning(r.pos);
10707                 st.width = r.width;
10708                 st.height = r.height;
10709
10710                 el.afterFx(o);
10711             };
10712
10713             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10714             switch(anchor.toLowerCase()){
10715                 case "t":
10716                     pt.by = [0, -h];
10717                 break;
10718                 case "l":
10719                     pt.by = [-w, 0];
10720                 break;
10721                 case "r":
10722                     pt.by = [w, 0];
10723                 break;
10724                 case "b":
10725                     pt.by = [0, h];
10726                 break;
10727                 case "tl":
10728                     pt.by = [-w, -h];
10729                 break;
10730                 case "bl":
10731                     pt.by = [-w, h];
10732                 break;
10733                 case "br":
10734                     pt.by = [w, h];
10735                 break;
10736                 case "tr":
10737                     pt.by = [w, -h];
10738                 break;
10739             }
10740
10741             arguments.callee.anim = this.fxanim(a,
10742                 o,
10743                 'motion',
10744                 .5,
10745                 "easeOut", after);
10746         });
10747         return this;
10748     },
10749
10750         /**
10751          * Ensures that all effects queued after syncFx is called on the element are
10752          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10753          * @return {Roo.Element} The Element
10754          */
10755     syncFx : function(){
10756         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10757             block : false,
10758             concurrent : true,
10759             stopFx : false
10760         });
10761         return this;
10762     },
10763
10764         /**
10765          * Ensures that all effects queued after sequenceFx is called on the element are
10766          * run in sequence.  This is the opposite of {@link #syncFx}.
10767          * @return {Roo.Element} The Element
10768          */
10769     sequenceFx : function(){
10770         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10771             block : false,
10772             concurrent : false,
10773             stopFx : false
10774         });
10775         return this;
10776     },
10777
10778         /* @private */
10779     nextFx : function(){
10780         var ef = this.fxQueue[0];
10781         if(ef){
10782             ef.call(this);
10783         }
10784     },
10785
10786         /**
10787          * Returns true if the element has any effects actively running or queued, else returns false.
10788          * @return {Boolean} True if element has active effects, else false
10789          */
10790     hasActiveFx : function(){
10791         return this.fxQueue && this.fxQueue[0];
10792     },
10793
10794         /**
10795          * Stops any running effects and clears the element's internal effects queue if it contains
10796          * any additional effects that haven't started yet.
10797          * @return {Roo.Element} The Element
10798          */
10799     stopFx : function(){
10800         if(this.hasActiveFx()){
10801             var cur = this.fxQueue[0];
10802             if(cur && cur.anim && cur.anim.isAnimated()){
10803                 this.fxQueue = [cur]; // clear out others
10804                 cur.anim.stop(true);
10805             }
10806         }
10807         return this;
10808     },
10809
10810         /* @private */
10811     beforeFx : function(o){
10812         if(this.hasActiveFx() && !o.concurrent){
10813            if(o.stopFx){
10814                this.stopFx();
10815                return true;
10816            }
10817            return false;
10818         }
10819         return true;
10820     },
10821
10822         /**
10823          * Returns true if the element is currently blocking so that no other effect can be queued
10824          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10825          * used to ensure that an effect initiated by a user action runs to completion prior to the
10826          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10827          * @return {Boolean} True if blocking, else false
10828          */
10829     hasFxBlock : function(){
10830         var q = this.fxQueue;
10831         return q && q[0] && q[0].block;
10832     },
10833
10834         /* @private */
10835     queueFx : function(o, fn){
10836         if(!this.fxQueue){
10837             this.fxQueue = [];
10838         }
10839         if(!this.hasFxBlock()){
10840             Roo.applyIf(o, this.fxDefaults);
10841             if(!o.concurrent){
10842                 var run = this.beforeFx(o);
10843                 fn.block = o.block;
10844                 this.fxQueue.push(fn);
10845                 if(run){
10846                     this.nextFx();
10847                 }
10848             }else{
10849                 fn.call(this);
10850             }
10851         }
10852         return this;
10853     },
10854
10855         /* @private */
10856     fxWrap : function(pos, o, vis){
10857         var wrap;
10858         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10859             var wrapXY;
10860             if(o.fixPosition){
10861                 wrapXY = this.getXY();
10862             }
10863             var div = document.createElement("div");
10864             div.style.visibility = vis;
10865             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10866             wrap.setPositioning(pos);
10867             if(wrap.getStyle("position") == "static"){
10868                 wrap.position("relative");
10869             }
10870             this.clearPositioning('auto');
10871             wrap.clip();
10872             wrap.dom.appendChild(this.dom);
10873             if(wrapXY){
10874                 wrap.setXY(wrapXY);
10875             }
10876         }
10877         return wrap;
10878     },
10879
10880         /* @private */
10881     fxUnwrap : function(wrap, pos, o){
10882         this.clearPositioning();
10883         this.setPositioning(pos);
10884         if(!o.wrap){
10885             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10886             wrap.remove();
10887         }
10888     },
10889
10890         /* @private */
10891     getFxRestore : function(){
10892         var st = this.dom.style;
10893         return {pos: this.getPositioning(), width: st.width, height : st.height};
10894     },
10895
10896         /* @private */
10897     afterFx : function(o){
10898         if(o.afterStyle){
10899             this.applyStyles(o.afterStyle);
10900         }
10901         if(o.afterCls){
10902             this.addClass(o.afterCls);
10903         }
10904         if(o.remove === true){
10905             this.remove();
10906         }
10907         Roo.callback(o.callback, o.scope, [this]);
10908         if(!o.concurrent){
10909             this.fxQueue.shift();
10910             this.nextFx();
10911         }
10912     },
10913
10914         /* @private */
10915     getFxEl : function(){ // support for composite element fx
10916         return Roo.get(this.dom);
10917     },
10918
10919         /* @private */
10920     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10921         animType = animType || 'run';
10922         opt = opt || {};
10923         var anim = Roo.lib.Anim[animType](
10924             this.dom, args,
10925             (opt.duration || defaultDur) || .35,
10926             (opt.easing || defaultEase) || 'easeOut',
10927             function(){
10928                 Roo.callback(cb, this);
10929             },
10930             this
10931         );
10932         opt.anim = anim;
10933         return anim;
10934     }
10935 };
10936
10937 // backwords compat
10938 Roo.Fx.resize = Roo.Fx.scale;
10939
10940 //When included, Roo.Fx is automatically applied to Element so that all basic
10941 //effects are available directly via the Element API
10942 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953
10954 /**
10955  * @class Roo.CompositeElement
10956  * Standard composite class. Creates a Roo.Element for every element in the collection.
10957  * <br><br>
10958  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10959  * actions will be performed on all the elements in this collection.</b>
10960  * <br><br>
10961  * All methods return <i>this</i> and can be chained.
10962  <pre><code>
10963  var els = Roo.select("#some-el div.some-class", true);
10964  // or select directly from an existing element
10965  var el = Roo.get('some-el');
10966  el.select('div.some-class', true);
10967
10968  els.setWidth(100); // all elements become 100 width
10969  els.hide(true); // all elements fade out and hide
10970  // or
10971  els.setWidth(100).hide(true);
10972  </code></pre>
10973  */
10974 Roo.CompositeElement = function(els){
10975     this.elements = [];
10976     this.addElements(els);
10977 };
10978 Roo.CompositeElement.prototype = {
10979     isComposite: true,
10980     addElements : function(els){
10981         if(!els) return this;
10982         if(typeof els == "string"){
10983             els = Roo.Element.selectorFunction(els);
10984         }
10985         var yels = this.elements;
10986         var index = yels.length-1;
10987         for(var i = 0, len = els.length; i < len; i++) {
10988                 yels[++index] = Roo.get(els[i]);
10989         }
10990         return this;
10991     },
10992
10993     /**
10994     * Clears this composite and adds the elements returned by the passed selector.
10995     * @param {String/Array} els A string CSS selector, an array of elements or an element
10996     * @return {CompositeElement} this
10997     */
10998     fill : function(els){
10999         this.elements = [];
11000         this.add(els);
11001         return this;
11002     },
11003
11004     /**
11005     * Filters this composite to only elements that match the passed selector.
11006     * @param {String} selector A string CSS selector
11007     * @param {Boolean} inverse return inverse filter (not matches)
11008     * @return {CompositeElement} this
11009     */
11010     filter : function(selector, inverse){
11011         var els = [];
11012         inverse = inverse || false;
11013         this.each(function(el){
11014             var match = inverse ? !el.is(selector) : el.is(selector);
11015             if(match){
11016                 els[els.length] = el.dom;
11017             }
11018         });
11019         this.fill(els);
11020         return this;
11021     },
11022
11023     invoke : function(fn, args){
11024         var els = this.elements;
11025         for(var i = 0, len = els.length; i < len; i++) {
11026                 Roo.Element.prototype[fn].apply(els[i], args);
11027         }
11028         return this;
11029     },
11030     /**
11031     * Adds elements to this composite.
11032     * @param {String/Array} els A string CSS selector, an array of elements or an element
11033     * @return {CompositeElement} this
11034     */
11035     add : function(els){
11036         if(typeof els == "string"){
11037             this.addElements(Roo.Element.selectorFunction(els));
11038         }else if(els.length !== undefined){
11039             this.addElements(els);
11040         }else{
11041             this.addElements([els]);
11042         }
11043         return this;
11044     },
11045     /**
11046     * Calls the passed function passing (el, this, index) for each element in this composite.
11047     * @param {Function} fn The function to call
11048     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11049     * @return {CompositeElement} this
11050     */
11051     each : function(fn, scope){
11052         var els = this.elements;
11053         for(var i = 0, len = els.length; i < len; i++){
11054             if(fn.call(scope || els[i], els[i], this, i) === false) {
11055                 break;
11056             }
11057         }
11058         return this;
11059     },
11060
11061     /**
11062      * Returns the Element object at the specified index
11063      * @param {Number} index
11064      * @return {Roo.Element}
11065      */
11066     item : function(index){
11067         return this.elements[index] || null;
11068     },
11069
11070     /**
11071      * Returns the first Element
11072      * @return {Roo.Element}
11073      */
11074     first : function(){
11075         return this.item(0);
11076     },
11077
11078     /**
11079      * Returns the last Element
11080      * @return {Roo.Element}
11081      */
11082     last : function(){
11083         return this.item(this.elements.length-1);
11084     },
11085
11086     /**
11087      * Returns the number of elements in this composite
11088      * @return Number
11089      */
11090     getCount : function(){
11091         return this.elements.length;
11092     },
11093
11094     /**
11095      * Returns true if this composite contains the passed element
11096      * @return Boolean
11097      */
11098     contains : function(el){
11099         return this.indexOf(el) !== -1;
11100     },
11101
11102     /**
11103      * Returns true if this composite contains the passed element
11104      * @return Boolean
11105      */
11106     indexOf : function(el){
11107         return this.elements.indexOf(Roo.get(el));
11108     },
11109
11110
11111     /**
11112     * Removes the specified element(s).
11113     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11114     * or an array of any of those.
11115     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11116     * @return {CompositeElement} this
11117     */
11118     removeElement : function(el, removeDom){
11119         if(el instanceof Array){
11120             for(var i = 0, len = el.length; i < len; i++){
11121                 this.removeElement(el[i]);
11122             }
11123             return this;
11124         }
11125         var index = typeof el == 'number' ? el : this.indexOf(el);
11126         if(index !== -1){
11127             if(removeDom){
11128                 var d = this.elements[index];
11129                 if(d.dom){
11130                     d.remove();
11131                 }else{
11132                     d.parentNode.removeChild(d);
11133                 }
11134             }
11135             this.elements.splice(index, 1);
11136         }
11137         return this;
11138     },
11139
11140     /**
11141     * Replaces the specified element with the passed element.
11142     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11143     * to replace.
11144     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11145     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11146     * @return {CompositeElement} this
11147     */
11148     replaceElement : function(el, replacement, domReplace){
11149         var index = typeof el == 'number' ? el : this.indexOf(el);
11150         if(index !== -1){
11151             if(domReplace){
11152                 this.elements[index].replaceWith(replacement);
11153             }else{
11154                 this.elements.splice(index, 1, Roo.get(replacement))
11155             }
11156         }
11157         return this;
11158     },
11159
11160     /**
11161      * Removes all elements.
11162      */
11163     clear : function(){
11164         this.elements = [];
11165     }
11166 };
11167 (function(){
11168     Roo.CompositeElement.createCall = function(proto, fnName){
11169         if(!proto[fnName]){
11170             proto[fnName] = function(){
11171                 return this.invoke(fnName, arguments);
11172             };
11173         }
11174     };
11175     for(var fnName in Roo.Element.prototype){
11176         if(typeof Roo.Element.prototype[fnName] == "function"){
11177             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11178         }
11179     };
11180 })();
11181 /*
11182  * Based on:
11183  * Ext JS Library 1.1.1
11184  * Copyright(c) 2006-2007, Ext JS, LLC.
11185  *
11186  * Originally Released Under LGPL - original licence link has changed is not relivant.
11187  *
11188  * Fork - LGPL
11189  * <script type="text/javascript">
11190  */
11191
11192 /**
11193  * @class Roo.CompositeElementLite
11194  * @extends Roo.CompositeElement
11195  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11196  <pre><code>
11197  var els = Roo.select("#some-el div.some-class");
11198  // or select directly from an existing element
11199  var el = Roo.get('some-el');
11200  el.select('div.some-class');
11201
11202  els.setWidth(100); // all elements become 100 width
11203  els.hide(true); // all elements fade out and hide
11204  // or
11205  els.setWidth(100).hide(true);
11206  </code></pre><br><br>
11207  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11208  * actions will be performed on all the elements in this collection.</b>
11209  */
11210 Roo.CompositeElementLite = function(els){
11211     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11212     this.el = new Roo.Element.Flyweight();
11213 };
11214 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11215     addElements : function(els){
11216         if(els){
11217             if(els instanceof Array){
11218                 this.elements = this.elements.concat(els);
11219             }else{
11220                 var yels = this.elements;
11221                 var index = yels.length-1;
11222                 for(var i = 0, len = els.length; i < len; i++) {
11223                     yels[++index] = els[i];
11224                 }
11225             }
11226         }
11227         return this;
11228     },
11229     invoke : function(fn, args){
11230         var els = this.elements;
11231         var el = this.el;
11232         for(var i = 0, len = els.length; i < len; i++) {
11233             el.dom = els[i];
11234                 Roo.Element.prototype[fn].apply(el, args);
11235         }
11236         return this;
11237     },
11238     /**
11239      * Returns a flyweight Element of the dom element object at the specified index
11240      * @param {Number} index
11241      * @return {Roo.Element}
11242      */
11243     item : function(index){
11244         if(!this.elements[index]){
11245             return null;
11246         }
11247         this.el.dom = this.elements[index];
11248         return this.el;
11249     },
11250
11251     // fixes scope with flyweight
11252     addListener : function(eventName, handler, scope, opt){
11253         var els = this.elements;
11254         for(var i = 0, len = els.length; i < len; i++) {
11255             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11256         }
11257         return this;
11258     },
11259
11260     /**
11261     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11262     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11263     * a reference to the dom node, use el.dom.</b>
11264     * @param {Function} fn The function to call
11265     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11266     * @return {CompositeElement} this
11267     */
11268     each : function(fn, scope){
11269         var els = this.elements;
11270         var el = this.el;
11271         for(var i = 0, len = els.length; i < len; i++){
11272             el.dom = els[i];
11273                 if(fn.call(scope || el, el, this, i) === false){
11274                 break;
11275             }
11276         }
11277         return this;
11278     },
11279
11280     indexOf : function(el){
11281         return this.elements.indexOf(Roo.getDom(el));
11282     },
11283
11284     replaceElement : function(el, replacement, domReplace){
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             replacement = Roo.getDom(replacement);
11288             if(domReplace){
11289                 var d = this.elements[index];
11290                 d.parentNode.insertBefore(replacement, d);
11291                 d.parentNode.removeChild(d);
11292             }
11293             this.elements.splice(index, 1, replacement);
11294         }
11295         return this;
11296     }
11297 });
11298 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11299
11300 /*
11301  * Based on:
11302  * Ext JS Library 1.1.1
11303  * Copyright(c) 2006-2007, Ext JS, LLC.
11304  *
11305  * Originally Released Under LGPL - original licence link has changed is not relivant.
11306  *
11307  * Fork - LGPL
11308  * <script type="text/javascript">
11309  */
11310
11311  
11312
11313 /**
11314  * @class Roo.data.Connection
11315  * @extends Roo.util.Observable
11316  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11317  * either to a configured URL, or to a URL specified at request time.<br><br>
11318  * <p>
11319  * Requests made by this class are asynchronous, and will return immediately. No data from
11320  * the server will be available to the statement immediately following the {@link #request} call.
11321  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11322  * <p>
11323  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11324  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11325  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11326  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11327  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11328  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11329  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11330  * standard DOM methods.
11331  * @constructor
11332  * @param {Object} config a configuration object.
11333  */
11334 Roo.data.Connection = function(config){
11335     Roo.apply(this, config);
11336     this.addEvents({
11337         /**
11338          * @event beforerequest
11339          * Fires before a network request is made to retrieve a data object.
11340          * @param {Connection} conn This Connection object.
11341          * @param {Object} options The options config object passed to the {@link #request} method.
11342          */
11343         "beforerequest" : true,
11344         /**
11345          * @event requestcomplete
11346          * Fires if the request was successfully completed.
11347          * @param {Connection} conn This Connection object.
11348          * @param {Object} response The XHR object containing the response data.
11349          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11350          * @param {Object} options The options config object passed to the {@link #request} method.
11351          */
11352         "requestcomplete" : true,
11353         /**
11354          * @event requestexception
11355          * Fires if an error HTTP status was returned from the server.
11356          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11357          * @param {Connection} conn This Connection object.
11358          * @param {Object} response The XHR object containing the response data.
11359          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11360          * @param {Object} options The options config object passed to the {@link #request} method.
11361          */
11362         "requestexception" : true
11363     });
11364     Roo.data.Connection.superclass.constructor.call(this);
11365 };
11366
11367 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11368     /**
11369      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11370      */
11371     /**
11372      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11373      * extra parameters to each request made by this object. (defaults to undefined)
11374      */
11375     /**
11376      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11377      *  to each request made by this object. (defaults to undefined)
11378      */
11379     /**
11380      * @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)
11381      */
11382     /**
11383      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11384      */
11385     timeout : 30000,
11386     /**
11387      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11388      * @type Boolean
11389      */
11390     autoAbort:false,
11391
11392     /**
11393      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11394      * @type Boolean
11395      */
11396     disableCaching: true,
11397
11398     /**
11399      * Sends an HTTP request to a remote server.
11400      * @param {Object} options An object which may contain the following properties:<ul>
11401      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11402      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11403      * request, a url encoded string or a function to call to get either.</li>
11404      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11405      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11406      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11407      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11408      * <li>options {Object} The parameter to the request call.</li>
11409      * <li>success {Boolean} True if the request succeeded.</li>
11410      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11411      * </ul></li>
11412      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11413      * The callback is passed the following parameters:<ul>
11414      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11415      * <li>options {Object} The parameter to the request call.</li>
11416      * </ul></li>
11417      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11418      * The callback is passed the following parameters:<ul>
11419      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11420      * <li>options {Object} The parameter to the request call.</li>
11421      * </ul></li>
11422      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11423      * for the callback function. Defaults to the browser window.</li>
11424      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11425      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11426      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11427      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11428      * params for the post data. Any params will be appended to the URL.</li>
11429      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11430      * </ul>
11431      * @return {Number} transactionId
11432      */
11433     request : function(o){
11434         if(this.fireEvent("beforerequest", this, o) !== false){
11435             var p = o.params;
11436
11437             if(typeof p == "function"){
11438                 p = p.call(o.scope||window, o);
11439             }
11440             if(typeof p == "object"){
11441                 p = Roo.urlEncode(o.params);
11442             }
11443             if(this.extraParams){
11444                 var extras = Roo.urlEncode(this.extraParams);
11445                 p = p ? (p + '&' + extras) : extras;
11446             }
11447
11448             var url = o.url || this.url;
11449             if(typeof url == 'function'){
11450                 url = url.call(o.scope||window, o);
11451             }
11452
11453             if(o.form){
11454                 var form = Roo.getDom(o.form);
11455                 url = url || form.action;
11456
11457                 var enctype = form.getAttribute("enctype");
11458                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11459                     return this.doFormUpload(o, p, url);
11460                 }
11461                 var f = Roo.lib.Ajax.serializeForm(form);
11462                 p = p ? (p + '&' + f) : f;
11463             }
11464
11465             var hs = o.headers;
11466             if(this.defaultHeaders){
11467                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11468                 if(!o.headers){
11469                     o.headers = hs;
11470                 }
11471             }
11472
11473             var cb = {
11474                 success: this.handleResponse,
11475                 failure: this.handleFailure,
11476                 scope: this,
11477                 argument: {options: o},
11478                 timeout : o.timeout || this.timeout
11479             };
11480
11481             var method = o.method||this.method||(p ? "POST" : "GET");
11482
11483             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11484                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11485             }
11486
11487             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11488                 if(o.autoAbort){
11489                     this.abort();
11490                 }
11491             }else if(this.autoAbort !== false){
11492                 this.abort();
11493             }
11494
11495             if((method == 'GET' && p) || o.xmlData){
11496                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11497                 p = '';
11498             }
11499             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11500             return this.transId;
11501         }else{
11502             Roo.callback(o.callback, o.scope, [o, null, null]);
11503             return null;
11504         }
11505     },
11506
11507     /**
11508      * Determine whether this object has a request outstanding.
11509      * @param {Number} transactionId (Optional) defaults to the last transaction
11510      * @return {Boolean} True if there is an outstanding request.
11511      */
11512     isLoading : function(transId){
11513         if(transId){
11514             return Roo.lib.Ajax.isCallInProgress(transId);
11515         }else{
11516             return this.transId ? true : false;
11517         }
11518     },
11519
11520     /**
11521      * Aborts any outstanding request.
11522      * @param {Number} transactionId (Optional) defaults to the last transaction
11523      */
11524     abort : function(transId){
11525         if(transId || this.isLoading()){
11526             Roo.lib.Ajax.abort(transId || this.transId);
11527         }
11528     },
11529
11530     // private
11531     handleResponse : function(response){
11532         this.transId = false;
11533         var options = response.argument.options;
11534         response.argument = options ? options.argument : null;
11535         this.fireEvent("requestcomplete", this, response, options);
11536         Roo.callback(options.success, options.scope, [response, options]);
11537         Roo.callback(options.callback, options.scope, [options, true, response]);
11538     },
11539
11540     // private
11541     handleFailure : function(response, e){
11542         this.transId = false;
11543         var options = response.argument.options;
11544         response.argument = options ? options.argument : null;
11545         this.fireEvent("requestexception", this, response, options, e);
11546         Roo.callback(options.failure, options.scope, [response, options]);
11547         Roo.callback(options.callback, options.scope, [options, false, response]);
11548     },
11549
11550     // private
11551     doFormUpload : function(o, ps, url){
11552         var id = Roo.id();
11553         var frame = document.createElement('iframe');
11554         frame.id = id;
11555         frame.name = id;
11556         frame.className = 'x-hidden';
11557         if(Roo.isIE){
11558             frame.src = Roo.SSL_SECURE_URL;
11559         }
11560         document.body.appendChild(frame);
11561
11562         if(Roo.isIE){
11563            document.frames[id].name = id;
11564         }
11565
11566         var form = Roo.getDom(o.form);
11567         form.target = id;
11568         form.method = 'POST';
11569         form.enctype = form.encoding = 'multipart/form-data';
11570         if(url){
11571             form.action = url;
11572         }
11573
11574         var hiddens, hd;
11575         if(ps){ // add dynamic params
11576             hiddens = [];
11577             ps = Roo.urlDecode(ps, false);
11578             for(var k in ps){
11579                 if(ps.hasOwnProperty(k)){
11580                     hd = document.createElement('input');
11581                     hd.type = 'hidden';
11582                     hd.name = k;
11583                     hd.value = ps[k];
11584                     form.appendChild(hd);
11585                     hiddens.push(hd);
11586                 }
11587             }
11588         }
11589
11590         function cb(){
11591             var r = {  // bogus response object
11592                 responseText : '',
11593                 responseXML : null
11594             };
11595
11596             r.argument = o ? o.argument : null;
11597
11598             try { //
11599                 var doc;
11600                 if(Roo.isIE){
11601                     doc = frame.contentWindow.document;
11602                 }else {
11603                     doc = (frame.contentDocument || window.frames[id].document);
11604                 }
11605                 if(doc && doc.body){
11606                     r.responseText = doc.body.innerHTML;
11607                 }
11608                 if(doc && doc.XMLDocument){
11609                     r.responseXML = doc.XMLDocument;
11610                 }else {
11611                     r.responseXML = doc;
11612                 }
11613             }
11614             catch(e) {
11615                 // ignore
11616             }
11617
11618             Roo.EventManager.removeListener(frame, 'load', cb, this);
11619
11620             this.fireEvent("requestcomplete", this, r, o);
11621             Roo.callback(o.success, o.scope, [r, o]);
11622             Roo.callback(o.callback, o.scope, [o, true, r]);
11623
11624             setTimeout(function(){document.body.removeChild(frame);}, 100);
11625         }
11626
11627         Roo.EventManager.on(frame, 'load', cb, this);
11628         form.submit();
11629
11630         if(hiddens){ // remove dynamic params
11631             for(var i = 0, len = hiddens.length; i < len; i++){
11632                 form.removeChild(hiddens[i]);
11633             }
11634         }
11635     }
11636 });
11637 /*
11638  * Based on:
11639  * Ext JS Library 1.1.1
11640  * Copyright(c) 2006-2007, Ext JS, LLC.
11641  *
11642  * Originally Released Under LGPL - original licence link has changed is not relivant.
11643  *
11644  * Fork - LGPL
11645  * <script type="text/javascript">
11646  */
11647  
11648 /**
11649  * Global Ajax request class.
11650  * 
11651  * @class Roo.Ajax
11652  * @extends Roo.data.Connection
11653  * @static
11654  * 
11655  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11656  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11657  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11658  * @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)
11659  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11660  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11661  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11662  */
11663 Roo.Ajax = new Roo.data.Connection({
11664     // fix up the docs
11665     /**
11666      * @scope Roo.Ajax
11667      * @type {Boolear} 
11668      */
11669     autoAbort : false,
11670
11671     /**
11672      * Serialize the passed form into a url encoded string
11673      * @scope Roo.Ajax
11674      * @param {String/HTMLElement} form
11675      * @return {String}
11676      */
11677     serializeForm : function(form){
11678         return Roo.lib.Ajax.serializeForm(form);
11679     }
11680 });/*
11681  * Based on:
11682  * Ext JS Library 1.1.1
11683  * Copyright(c) 2006-2007, Ext JS, LLC.
11684  *
11685  * Originally Released Under LGPL - original licence link has changed is not relivant.
11686  *
11687  * Fork - LGPL
11688  * <script type="text/javascript">
11689  */
11690
11691  
11692 /**
11693  * @class Roo.UpdateManager
11694  * @extends Roo.util.Observable
11695  * Provides AJAX-style update for Element object.<br><br>
11696  * Usage:<br>
11697  * <pre><code>
11698  * // Get it from a Roo.Element object
11699  * var el = Roo.get("foo");
11700  * var mgr = el.getUpdateManager();
11701  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11702  * ...
11703  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11704  * <br>
11705  * // or directly (returns the same UpdateManager instance)
11706  * var mgr = new Roo.UpdateManager("myElementId");
11707  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11708  * mgr.on("update", myFcnNeedsToKnow);
11709  * <br>
11710    // short handed call directly from the element object
11711    Roo.get("foo").load({
11712         url: "bar.php",
11713         scripts:true,
11714         params: "for=bar",
11715         text: "Loading Foo..."
11716    });
11717  * </code></pre>
11718  * @constructor
11719  * Create new UpdateManager directly.
11720  * @param {String/HTMLElement/Roo.Element} el The element to update
11721  * @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).
11722  */
11723 Roo.UpdateManager = function(el, forceNew){
11724     el = Roo.get(el);
11725     if(!forceNew && el.updateManager){
11726         return el.updateManager;
11727     }
11728     /**
11729      * The Element object
11730      * @type Roo.Element
11731      */
11732     this.el = el;
11733     /**
11734      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11735      * @type String
11736      */
11737     this.defaultUrl = null;
11738
11739     this.addEvents({
11740         /**
11741          * @event beforeupdate
11742          * Fired before an update is made, return false from your handler and the update is cancelled.
11743          * @param {Roo.Element} el
11744          * @param {String/Object/Function} url
11745          * @param {String/Object} params
11746          */
11747         "beforeupdate": true,
11748         /**
11749          * @event update
11750          * Fired after successful update is made.
11751          * @param {Roo.Element} el
11752          * @param {Object} oResponseObject The response Object
11753          */
11754         "update": true,
11755         /**
11756          * @event failure
11757          * Fired on update failure.
11758          * @param {Roo.Element} el
11759          * @param {Object} oResponseObject The response Object
11760          */
11761         "failure": true
11762     });
11763     var d = Roo.UpdateManager.defaults;
11764     /**
11765      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11766      * @type String
11767      */
11768     this.sslBlankUrl = d.sslBlankUrl;
11769     /**
11770      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11771      * @type Boolean
11772      */
11773     this.disableCaching = d.disableCaching;
11774     /**
11775      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11776      * @type String
11777      */
11778     this.indicatorText = d.indicatorText;
11779     /**
11780      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11781      * @type String
11782      */
11783     this.showLoadIndicator = d.showLoadIndicator;
11784     /**
11785      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11786      * @type Number
11787      */
11788     this.timeout = d.timeout;
11789
11790     /**
11791      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11792      * @type Boolean
11793      */
11794     this.loadScripts = d.loadScripts;
11795
11796     /**
11797      * Transaction object of current executing transaction
11798      */
11799     this.transaction = null;
11800
11801     /**
11802      * @private
11803      */
11804     this.autoRefreshProcId = null;
11805     /**
11806      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11807      * @type Function
11808      */
11809     this.refreshDelegate = this.refresh.createDelegate(this);
11810     /**
11811      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11812      * @type Function
11813      */
11814     this.updateDelegate = this.update.createDelegate(this);
11815     /**
11816      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11817      * @type Function
11818      */
11819     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11820     /**
11821      * @private
11822      */
11823     this.successDelegate = this.processSuccess.createDelegate(this);
11824     /**
11825      * @private
11826      */
11827     this.failureDelegate = this.processFailure.createDelegate(this);
11828
11829     if(!this.renderer){
11830      /**
11831       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11832       */
11833     this.renderer = new Roo.UpdateManager.BasicRenderer();
11834     }
11835     
11836     Roo.UpdateManager.superclass.constructor.call(this);
11837 };
11838
11839 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11840     /**
11841      * Get the Element this UpdateManager is bound to
11842      * @return {Roo.Element} The element
11843      */
11844     getEl : function(){
11845         return this.el;
11846     },
11847     /**
11848      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11849      * @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:
11850 <pre><code>
11851 um.update({<br/>
11852     url: "your-url.php",<br/>
11853     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11854     callback: yourFunction,<br/>
11855     scope: yourObject, //(optional scope)  <br/>
11856     discardUrl: false, <br/>
11857     nocache: false,<br/>
11858     text: "Loading...",<br/>
11859     timeout: 30,<br/>
11860     scripts: false<br/>
11861 });
11862 </code></pre>
11863      * The only required property is url. The optional properties nocache, text and scripts
11864      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11865      * @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}
11866      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11867      * @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.
11868      */
11869     update : function(url, params, callback, discardUrl){
11870         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11871             var method = this.method,
11872                 cfg;
11873             if(typeof url == "object"){ // must be config object
11874                 cfg = url;
11875                 url = cfg.url;
11876                 params = params || cfg.params;
11877                 callback = callback || cfg.callback;
11878                 discardUrl = discardUrl || cfg.discardUrl;
11879                 if(callback && cfg.scope){
11880                     callback = callback.createDelegate(cfg.scope);
11881                 }
11882                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11883                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11884                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11885                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11886                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11887             }
11888             this.showLoading();
11889             if(!discardUrl){
11890                 this.defaultUrl = url;
11891             }
11892             if(typeof url == "function"){
11893                 url = url.call(this);
11894             }
11895
11896             method = method || (params ? "POST" : "GET");
11897             if(method == "GET"){
11898                 url = this.prepareUrl(url);
11899             }
11900
11901             var o = Roo.apply(cfg ||{}, {
11902                 url : url,
11903                 params: params,
11904                 success: this.successDelegate,
11905                 failure: this.failureDelegate,
11906                 callback: undefined,
11907                 timeout: (this.timeout*1000),
11908                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11909             });
11910             Roo.log("updated manager called with timeout of " + o.timeout);
11911             this.transaction = Roo.Ajax.request(o);
11912         }
11913     },
11914
11915     /**
11916      * 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.
11917      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11918      * @param {String/HTMLElement} form The form Id or form element
11919      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11920      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11921      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11922      */
11923     formUpdate : function(form, url, reset, callback){
11924         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11925             if(typeof url == "function"){
11926                 url = url.call(this);
11927             }
11928             form = Roo.getDom(form);
11929             this.transaction = Roo.Ajax.request({
11930                 form: form,
11931                 url:url,
11932                 success: this.successDelegate,
11933                 failure: this.failureDelegate,
11934                 timeout: (this.timeout*1000),
11935                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11936             });
11937             this.showLoading.defer(1, this);
11938         }
11939     },
11940
11941     /**
11942      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11943      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11944      */
11945     refresh : function(callback){
11946         if(this.defaultUrl == null){
11947             return;
11948         }
11949         this.update(this.defaultUrl, null, callback, true);
11950     },
11951
11952     /**
11953      * Set this element to auto refresh.
11954      * @param {Number} interval How often to update (in seconds).
11955      * @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)
11956      * @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}
11957      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11958      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11959      */
11960     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11961         if(refreshNow){
11962             this.update(url || this.defaultUrl, params, callback, true);
11963         }
11964         if(this.autoRefreshProcId){
11965             clearInterval(this.autoRefreshProcId);
11966         }
11967         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11968     },
11969
11970     /**
11971      * Stop auto refresh on this element.
11972      */
11973      stopAutoRefresh : function(){
11974         if(this.autoRefreshProcId){
11975             clearInterval(this.autoRefreshProcId);
11976             delete this.autoRefreshProcId;
11977         }
11978     },
11979
11980     isAutoRefreshing : function(){
11981        return this.autoRefreshProcId ? true : false;
11982     },
11983     /**
11984      * Called to update the element to "Loading" state. Override to perform custom action.
11985      */
11986     showLoading : function(){
11987         if(this.showLoadIndicator){
11988             this.el.update(this.indicatorText);
11989         }
11990     },
11991
11992     /**
11993      * Adds unique parameter to query string if disableCaching = true
11994      * @private
11995      */
11996     prepareUrl : function(url){
11997         if(this.disableCaching){
11998             var append = "_dc=" + (new Date().getTime());
11999             if(url.indexOf("?") !== -1){
12000                 url += "&" + append;
12001             }else{
12002                 url += "?" + append;
12003             }
12004         }
12005         return url;
12006     },
12007
12008     /**
12009      * @private
12010      */
12011     processSuccess : function(response){
12012         this.transaction = null;
12013         if(response.argument.form && response.argument.reset){
12014             try{ // put in try/catch since some older FF releases had problems with this
12015                 response.argument.form.reset();
12016             }catch(e){}
12017         }
12018         if(this.loadScripts){
12019             this.renderer.render(this.el, response, this,
12020                 this.updateComplete.createDelegate(this, [response]));
12021         }else{
12022             this.renderer.render(this.el, response, this);
12023             this.updateComplete(response);
12024         }
12025     },
12026
12027     updateComplete : function(response){
12028         this.fireEvent("update", this.el, response);
12029         if(typeof response.argument.callback == "function"){
12030             response.argument.callback(this.el, true, response);
12031         }
12032     },
12033
12034     /**
12035      * @private
12036      */
12037     processFailure : function(response){
12038         this.transaction = null;
12039         this.fireEvent("failure", this.el, response);
12040         if(typeof response.argument.callback == "function"){
12041             response.argument.callback(this.el, false, response);
12042         }
12043     },
12044
12045     /**
12046      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12047      * @param {Object} renderer The object implementing the render() method
12048      */
12049     setRenderer : function(renderer){
12050         this.renderer = renderer;
12051     },
12052
12053     getRenderer : function(){
12054        return this.renderer;
12055     },
12056
12057     /**
12058      * Set the defaultUrl used for updates
12059      * @param {String/Function} defaultUrl The url or a function to call to get the url
12060      */
12061     setDefaultUrl : function(defaultUrl){
12062         this.defaultUrl = defaultUrl;
12063     },
12064
12065     /**
12066      * Aborts the executing transaction
12067      */
12068     abort : function(){
12069         if(this.transaction){
12070             Roo.Ajax.abort(this.transaction);
12071         }
12072     },
12073
12074     /**
12075      * Returns true if an update is in progress
12076      * @return {Boolean}
12077      */
12078     isUpdating : function(){
12079         if(this.transaction){
12080             return Roo.Ajax.isLoading(this.transaction);
12081         }
12082         return false;
12083     }
12084 });
12085
12086 /**
12087  * @class Roo.UpdateManager.defaults
12088  * @static (not really - but it helps the doc tool)
12089  * The defaults collection enables customizing the default properties of UpdateManager
12090  */
12091    Roo.UpdateManager.defaults = {
12092        /**
12093          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12094          * @type Number
12095          */
12096          timeout : 30,
12097
12098          /**
12099          * True to process scripts by default (Defaults to false).
12100          * @type Boolean
12101          */
12102         loadScripts : false,
12103
12104         /**
12105         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12106         * @type String
12107         */
12108         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12109         /**
12110          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12111          * @type Boolean
12112          */
12113         disableCaching : false,
12114         /**
12115          * Whether to show indicatorText when loading (Defaults to true).
12116          * @type Boolean
12117          */
12118         showLoadIndicator : true,
12119         /**
12120          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12121          * @type String
12122          */
12123         indicatorText : '<div class="loading-indicator">Loading...</div>'
12124    };
12125
12126 /**
12127  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12128  *Usage:
12129  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12130  * @param {String/HTMLElement/Roo.Element} el The element to update
12131  * @param {String} url The url
12132  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12133  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12134  * @static
12135  * @deprecated
12136  * @member Roo.UpdateManager
12137  */
12138 Roo.UpdateManager.updateElement = function(el, url, params, options){
12139     var um = Roo.get(el, true).getUpdateManager();
12140     Roo.apply(um, options);
12141     um.update(url, params, options ? options.callback : null);
12142 };
12143 // alias for backwards compat
12144 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12145 /**
12146  * @class Roo.UpdateManager.BasicRenderer
12147  * Default Content renderer. Updates the elements innerHTML with the responseText.
12148  */
12149 Roo.UpdateManager.BasicRenderer = function(){};
12150
12151 Roo.UpdateManager.BasicRenderer.prototype = {
12152     /**
12153      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12154      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12155      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12156      * @param {Roo.Element} el The element being rendered
12157      * @param {Object} response The YUI Connect response object
12158      * @param {UpdateManager} updateManager The calling update manager
12159      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12160      */
12161      render : function(el, response, updateManager, callback){
12162         el.update(response.responseText, updateManager.loadScripts, callback);
12163     }
12164 };
12165 /*
12166  * Based on:
12167  * Roo JS
12168  * (c)) Alan Knowles
12169  * Licence : LGPL
12170  */
12171
12172
12173 /**
12174  * @class Roo.DomTemplate
12175  * @extends Roo.Template
12176  * An effort at a dom based template engine..
12177  *
12178  * Similar to XTemplate, except it uses dom parsing to create the template..
12179  *
12180  * Supported features:
12181  *
12182  *  Tags:
12183
12184 <pre><code>
12185       {a_variable} - output encoded.
12186       {a_variable.format:("Y-m-d")} - call a method on the variable
12187       {a_variable:raw} - unencoded output
12188       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12189       {a_variable:this.method_on_template(...)} - call a method on the template object.
12190  
12191 </code></pre>
12192  *  The tpl tag:
12193 <pre><code>
12194         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12195         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12196         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12197         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12198   
12199 </code></pre>
12200  *      
12201  */
12202 Roo.DomTemplate = function()
12203 {
12204      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12205      if (this.html) {
12206         this.compile();
12207      }
12208 };
12209
12210
12211 Roo.extend(Roo.DomTemplate, Roo.Template, {
12212     /**
12213      * id counter for sub templates.
12214      */
12215     id : 0,
12216     /**
12217      * flag to indicate if dom parser is inside a pre,
12218      * it will strip whitespace if not.
12219      */
12220     inPre : false,
12221     
12222     /**
12223      * The various sub templates
12224      */
12225     tpls : false,
12226     
12227     
12228     
12229     /**
12230      *
12231      * basic tag replacing syntax
12232      * WORD:WORD()
12233      *
12234      * // you can fake an object call by doing this
12235      *  x.t:(test,tesT) 
12236      * 
12237      */
12238     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12239     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12240     
12241     iterChild : function (node, method) {
12242         
12243         var oldPre = this.inPre;
12244         if (node.tagName == 'PRE') {
12245             this.inPre = true;
12246         }
12247         for( var i = 0; i < node.childNodes.length; i++) {
12248             method.call(this, node.childNodes[i]);
12249         }
12250         this.inPre = oldPre;
12251     },
12252     
12253     
12254     
12255     /**
12256      * compile the template
12257      *
12258      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12259      *
12260      */
12261     compile: function()
12262     {
12263         var s = this.html;
12264         
12265         // covert the html into DOM...
12266         var doc = false;
12267         var div =false;
12268         try {
12269             doc = document.implementation.createHTMLDocument("");
12270             doc.documentElement.innerHTML =   this.html  ;
12271             div = doc.documentElement;
12272         } catch (e) {
12273             // old IE... - nasty -- it causes all sorts of issues.. with
12274             // images getting pulled from server..
12275             div = document.createElement('div');
12276             div.innerHTML = this.html;
12277         }
12278         //doc.documentElement.innerHTML = htmlBody
12279          
12280         
12281         
12282         this.tpls = [];
12283         var _t = this;
12284         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12285         
12286         var tpls = this.tpls;
12287         
12288         // create a top level template from the snippet..
12289         
12290         //Roo.log(div.innerHTML);
12291         
12292         var tpl = {
12293             uid : 'master',
12294             id : this.id++,
12295             attr : false,
12296             value : false,
12297             body : div.innerHTML,
12298             
12299             forCall : false,
12300             execCall : false,
12301             dom : div,
12302             isTop : true
12303             
12304         };
12305         tpls.unshift(tpl);
12306         
12307         
12308         // compile them...
12309         this.tpls = [];
12310         Roo.each(tpls, function(tp){
12311             this.compileTpl(tp);
12312             this.tpls[tp.id] = tp;
12313         }, this);
12314         
12315         this.master = tpls[0];
12316         return this;
12317         
12318         
12319     },
12320     
12321     compileNode : function(node, istop) {
12322         // test for
12323         //Roo.log(node);
12324         
12325         
12326         // skip anything not a tag..
12327         if (node.nodeType != 1) {
12328             if (node.nodeType == 3 && !this.inPre) {
12329                 // reduce white space..
12330                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12331                 
12332             }
12333             return;
12334         }
12335         
12336         var tpl = {
12337             uid : false,
12338             id : false,
12339             attr : false,
12340             value : false,
12341             body : '',
12342             
12343             forCall : false,
12344             execCall : false,
12345             dom : false,
12346             isTop : istop
12347             
12348             
12349         };
12350         
12351         
12352         switch(true) {
12353             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12354             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12355             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12356             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12357             // no default..
12358         }
12359         
12360         
12361         if (!tpl.attr) {
12362             // just itterate children..
12363             this.iterChild(node,this.compileNode);
12364             return;
12365         }
12366         tpl.uid = this.id++;
12367         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12368         node.removeAttribute('roo-'+ tpl.attr);
12369         if (tpl.attr != 'name') {
12370             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12371             node.parentNode.replaceChild(placeholder,  node);
12372         } else {
12373             
12374             var placeholder =  document.createElement('span');
12375             placeholder.className = 'roo-tpl-' + tpl.value;
12376             node.parentNode.replaceChild(placeholder,  node);
12377         }
12378         
12379         // parent now sees '{domtplXXXX}
12380         this.iterChild(node,this.compileNode);
12381         
12382         // we should now have node body...
12383         var div = document.createElement('div');
12384         div.appendChild(node);
12385         tpl.dom = node;
12386         // this has the unfortunate side effect of converting tagged attributes
12387         // eg. href="{...}" into %7C...%7D
12388         // this has been fixed by searching for those combo's although it's a bit hacky..
12389         
12390         
12391         tpl.body = div.innerHTML;
12392         
12393         
12394          
12395         tpl.id = tpl.uid;
12396         switch(tpl.attr) {
12397             case 'for' :
12398                 switch (tpl.value) {
12399                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12400                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12401                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12402                 }
12403                 break;
12404             
12405             case 'exec':
12406                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12407                 break;
12408             
12409             case 'if':     
12410                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12411                 break;
12412             
12413             case 'name':
12414                 tpl.id  = tpl.value; // replace non characters???
12415                 break;
12416             
12417         }
12418         
12419         
12420         this.tpls.push(tpl);
12421         
12422         
12423         
12424     },
12425     
12426     
12427     
12428     
12429     /**
12430      * Compile a segment of the template into a 'sub-template'
12431      *
12432      * 
12433      * 
12434      *
12435      */
12436     compileTpl : function(tpl)
12437     {
12438         var fm = Roo.util.Format;
12439         var useF = this.disableFormats !== true;
12440         
12441         var sep = Roo.isGecko ? "+\n" : ",\n";
12442         
12443         var undef = function(str) {
12444             Roo.debug && Roo.log("Property not found :"  + str);
12445             return '';
12446         };
12447           
12448         //Roo.log(tpl.body);
12449         
12450         
12451         
12452         var fn = function(m, lbrace, name, format, args)
12453         {
12454             //Roo.log("ARGS");
12455             //Roo.log(arguments);
12456             args = args ? args.replace(/\\'/g,"'") : args;
12457             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12458             if (typeof(format) == 'undefined') {
12459                 format =  'htmlEncode'; 
12460             }
12461             if (format == 'raw' ) {
12462                 format = false;
12463             }
12464             
12465             if(name.substr(0, 6) == 'domtpl'){
12466                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12467             }
12468             
12469             // build an array of options to determine if value is undefined..
12470             
12471             // basically get 'xxxx.yyyy' then do
12472             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12473             //    (function () { Roo.log("Property not found"); return ''; })() :
12474             //    ......
12475             
12476             var udef_ar = [];
12477             var lookfor = '';
12478             Roo.each(name.split('.'), function(st) {
12479                 lookfor += (lookfor.length ? '.': '') + st;
12480                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12481             });
12482             
12483             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12484             
12485             
12486             if(format && useF){
12487                 
12488                 args = args ? ',' + args : "";
12489                  
12490                 if(format.substr(0, 5) != "this."){
12491                     format = "fm." + format + '(';
12492                 }else{
12493                     format = 'this.call("'+ format.substr(5) + '", ';
12494                     args = ", values";
12495                 }
12496                 
12497                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12498             }
12499              
12500             if (args && args.length) {
12501                 // called with xxyx.yuu:(test,test)
12502                 // change to ()
12503                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12504             }
12505             // raw.. - :raw modifier..
12506             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12507             
12508         };
12509         var body;
12510         // branched to use + in gecko and [].join() in others
12511         if(Roo.isGecko){
12512             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12513                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12514                     "';};};";
12515         }else{
12516             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12517             body.push(tpl.body.replace(/(\r\n|\n)/g,
12518                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12519             body.push("'].join('');};};");
12520             body = body.join('');
12521         }
12522         
12523         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12524        
12525         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12526         eval(body);
12527         
12528         return this;
12529     },
12530      
12531     /**
12532      * same as applyTemplate, except it's done to one of the subTemplates
12533      * when using named templates, you can do:
12534      *
12535      * var str = pl.applySubTemplate('your-name', values);
12536      *
12537      * 
12538      * @param {Number} id of the template
12539      * @param {Object} values to apply to template
12540      * @param {Object} parent (normaly the instance of this object)
12541      */
12542     applySubTemplate : function(id, values, parent)
12543     {
12544         
12545         
12546         var t = this.tpls[id];
12547         
12548         
12549         try { 
12550             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12551                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12552                 return '';
12553             }
12554         } catch(e) {
12555             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12556             Roo.log(values);
12557           
12558             return '';
12559         }
12560         try { 
12561             
12562             if(t.execCall && t.execCall.call(this, values, parent)){
12563                 return '';
12564             }
12565         } catch(e) {
12566             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12567             Roo.log(values);
12568             return '';
12569         }
12570         
12571         try {
12572             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12573             parent = t.target ? values : parent;
12574             if(t.forCall && vs instanceof Array){
12575                 var buf = [];
12576                 for(var i = 0, len = vs.length; i < len; i++){
12577                     try {
12578                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12579                     } catch (e) {
12580                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12581                         Roo.log(e.body);
12582                         //Roo.log(t.compiled);
12583                         Roo.log(vs[i]);
12584                     }   
12585                 }
12586                 return buf.join('');
12587             }
12588         } catch (e) {
12589             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12590             Roo.log(values);
12591             return '';
12592         }
12593         try {
12594             return t.compiled.call(this, vs, parent);
12595         } catch (e) {
12596             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12597             Roo.log(e.body);
12598             //Roo.log(t.compiled);
12599             Roo.log(values);
12600             return '';
12601         }
12602     },
12603
12604    
12605
12606     applyTemplate : function(values){
12607         return this.master.compiled.call(this, values, {});
12608         //var s = this.subs;
12609     },
12610
12611     apply : function(){
12612         return this.applyTemplate.apply(this, arguments);
12613     }
12614
12615  });
12616
12617 Roo.DomTemplate.from = function(el){
12618     el = Roo.getDom(el);
12619     return new Roo.Domtemplate(el.value || el.innerHTML);
12620 };/*
12621  * Based on:
12622  * Ext JS Library 1.1.1
12623  * Copyright(c) 2006-2007, Ext JS, LLC.
12624  *
12625  * Originally Released Under LGPL - original licence link has changed is not relivant.
12626  *
12627  * Fork - LGPL
12628  * <script type="text/javascript">
12629  */
12630
12631 /**
12632  * @class Roo.util.DelayedTask
12633  * Provides a convenient method of performing setTimeout where a new
12634  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12635  * You can use this class to buffer
12636  * the keypress events for a certain number of milliseconds, and perform only if they stop
12637  * for that amount of time.
12638  * @constructor The parameters to this constructor serve as defaults and are not required.
12639  * @param {Function} fn (optional) The default function to timeout
12640  * @param {Object} scope (optional) The default scope of that timeout
12641  * @param {Array} args (optional) The default Array of arguments
12642  */
12643 Roo.util.DelayedTask = function(fn, scope, args){
12644     var id = null, d, t;
12645
12646     var call = function(){
12647         var now = new Date().getTime();
12648         if(now - t >= d){
12649             clearInterval(id);
12650             id = null;
12651             fn.apply(scope, args || []);
12652         }
12653     };
12654     /**
12655      * Cancels any pending timeout and queues a new one
12656      * @param {Number} delay The milliseconds to delay
12657      * @param {Function} newFn (optional) Overrides function passed to constructor
12658      * @param {Object} newScope (optional) Overrides scope passed to constructor
12659      * @param {Array} newArgs (optional) Overrides args passed to constructor
12660      */
12661     this.delay = function(delay, newFn, newScope, newArgs){
12662         if(id && delay != d){
12663             this.cancel();
12664         }
12665         d = delay;
12666         t = new Date().getTime();
12667         fn = newFn || fn;
12668         scope = newScope || scope;
12669         args = newArgs || args;
12670         if(!id){
12671             id = setInterval(call, d);
12672         }
12673     };
12674
12675     /**
12676      * Cancel the last queued timeout
12677      */
12678     this.cancel = function(){
12679         if(id){
12680             clearInterval(id);
12681             id = null;
12682         }
12683     };
12684 };/*
12685  * Based on:
12686  * Ext JS Library 1.1.1
12687  * Copyright(c) 2006-2007, Ext JS, LLC.
12688  *
12689  * Originally Released Under LGPL - original licence link has changed is not relivant.
12690  *
12691  * Fork - LGPL
12692  * <script type="text/javascript">
12693  */
12694  
12695  
12696 Roo.util.TaskRunner = function(interval){
12697     interval = interval || 10;
12698     var tasks = [], removeQueue = [];
12699     var id = 0;
12700     var running = false;
12701
12702     var stopThread = function(){
12703         running = false;
12704         clearInterval(id);
12705         id = 0;
12706     };
12707
12708     var startThread = function(){
12709         if(!running){
12710             running = true;
12711             id = setInterval(runTasks, interval);
12712         }
12713     };
12714
12715     var removeTask = function(task){
12716         removeQueue.push(task);
12717         if(task.onStop){
12718             task.onStop();
12719         }
12720     };
12721
12722     var runTasks = function(){
12723         if(removeQueue.length > 0){
12724             for(var i = 0, len = removeQueue.length; i < len; i++){
12725                 tasks.remove(removeQueue[i]);
12726             }
12727             removeQueue = [];
12728             if(tasks.length < 1){
12729                 stopThread();
12730                 return;
12731             }
12732         }
12733         var now = new Date().getTime();
12734         for(var i = 0, len = tasks.length; i < len; ++i){
12735             var t = tasks[i];
12736             var itime = now - t.taskRunTime;
12737             if(t.interval <= itime){
12738                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12739                 t.taskRunTime = now;
12740                 if(rt === false || t.taskRunCount === t.repeat){
12741                     removeTask(t);
12742                     return;
12743                 }
12744             }
12745             if(t.duration && t.duration <= (now - t.taskStartTime)){
12746                 removeTask(t);
12747             }
12748         }
12749     };
12750
12751     /**
12752      * Queues a new task.
12753      * @param {Object} task
12754      */
12755     this.start = function(task){
12756         tasks.push(task);
12757         task.taskStartTime = new Date().getTime();
12758         task.taskRunTime = 0;
12759         task.taskRunCount = 0;
12760         startThread();
12761         return task;
12762     };
12763
12764     this.stop = function(task){
12765         removeTask(task);
12766         return task;
12767     };
12768
12769     this.stopAll = function(){
12770         stopThread();
12771         for(var i = 0, len = tasks.length; i < len; i++){
12772             if(tasks[i].onStop){
12773                 tasks[i].onStop();
12774             }
12775         }
12776         tasks = [];
12777         removeQueue = [];
12778     };
12779 };
12780
12781 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12782  * Based on:
12783  * Ext JS Library 1.1.1
12784  * Copyright(c) 2006-2007, Ext JS, LLC.
12785  *
12786  * Originally Released Under LGPL - original licence link has changed is not relivant.
12787  *
12788  * Fork - LGPL
12789  * <script type="text/javascript">
12790  */
12791
12792  
12793 /**
12794  * @class Roo.util.MixedCollection
12795  * @extends Roo.util.Observable
12796  * A Collection class that maintains both numeric indexes and keys and exposes events.
12797  * @constructor
12798  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12799  * collection (defaults to false)
12800  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12801  * and return the key value for that item.  This is used when available to look up the key on items that
12802  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12803  * equivalent to providing an implementation for the {@link #getKey} method.
12804  */
12805 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12806     this.items = [];
12807     this.map = {};
12808     this.keys = [];
12809     this.length = 0;
12810     this.addEvents({
12811         /**
12812          * @event clear
12813          * Fires when the collection is cleared.
12814          */
12815         "clear" : true,
12816         /**
12817          * @event add
12818          * Fires when an item is added to the collection.
12819          * @param {Number} index The index at which the item was added.
12820          * @param {Object} o The item added.
12821          * @param {String} key The key associated with the added item.
12822          */
12823         "add" : true,
12824         /**
12825          * @event replace
12826          * Fires when an item is replaced in the collection.
12827          * @param {String} key he key associated with the new added.
12828          * @param {Object} old The item being replaced.
12829          * @param {Object} new The new item.
12830          */
12831         "replace" : true,
12832         /**
12833          * @event remove
12834          * Fires when an item is removed from the collection.
12835          * @param {Object} o The item being removed.
12836          * @param {String} key (optional) The key associated with the removed item.
12837          */
12838         "remove" : true,
12839         "sort" : true
12840     });
12841     this.allowFunctions = allowFunctions === true;
12842     if(keyFn){
12843         this.getKey = keyFn;
12844     }
12845     Roo.util.MixedCollection.superclass.constructor.call(this);
12846 };
12847
12848 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12849     allowFunctions : false,
12850     
12851 /**
12852  * Adds an item to the collection.
12853  * @param {String} key The key to associate with the item
12854  * @param {Object} o The item to add.
12855  * @return {Object} The item added.
12856  */
12857     add : function(key, o){
12858         if(arguments.length == 1){
12859             o = arguments[0];
12860             key = this.getKey(o);
12861         }
12862         if(typeof key == "undefined" || key === null){
12863             this.length++;
12864             this.items.push(o);
12865             this.keys.push(null);
12866         }else{
12867             var old = this.map[key];
12868             if(old){
12869                 return this.replace(key, o);
12870             }
12871             this.length++;
12872             this.items.push(o);
12873             this.map[key] = o;
12874             this.keys.push(key);
12875         }
12876         this.fireEvent("add", this.length-1, o, key);
12877         return o;
12878     },
12879        
12880 /**
12881   * MixedCollection has a generic way to fetch keys if you implement getKey.
12882 <pre><code>
12883 // normal way
12884 var mc = new Roo.util.MixedCollection();
12885 mc.add(someEl.dom.id, someEl);
12886 mc.add(otherEl.dom.id, otherEl);
12887 //and so on
12888
12889 // using getKey
12890 var mc = new Roo.util.MixedCollection();
12891 mc.getKey = function(el){
12892    return el.dom.id;
12893 };
12894 mc.add(someEl);
12895 mc.add(otherEl);
12896
12897 // or via the constructor
12898 var mc = new Roo.util.MixedCollection(false, function(el){
12899    return el.dom.id;
12900 });
12901 mc.add(someEl);
12902 mc.add(otherEl);
12903 </code></pre>
12904  * @param o {Object} The item for which to find the key.
12905  * @return {Object} The key for the passed item.
12906  */
12907     getKey : function(o){
12908          return o.id; 
12909     },
12910    
12911 /**
12912  * Replaces an item in the collection.
12913  * @param {String} key The key associated with the item to replace, or the item to replace.
12914  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12915  * @return {Object}  The new item.
12916  */
12917     replace : function(key, o){
12918         if(arguments.length == 1){
12919             o = arguments[0];
12920             key = this.getKey(o);
12921         }
12922         var old = this.item(key);
12923         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12924              return this.add(key, o);
12925         }
12926         var index = this.indexOfKey(key);
12927         this.items[index] = o;
12928         this.map[key] = o;
12929         this.fireEvent("replace", key, old, o);
12930         return o;
12931     },
12932    
12933 /**
12934  * Adds all elements of an Array or an Object to the collection.
12935  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12936  * an Array of values, each of which are added to the collection.
12937  */
12938     addAll : function(objs){
12939         if(arguments.length > 1 || objs instanceof Array){
12940             var args = arguments.length > 1 ? arguments : objs;
12941             for(var i = 0, len = args.length; i < len; i++){
12942                 this.add(args[i]);
12943             }
12944         }else{
12945             for(var key in objs){
12946                 if(this.allowFunctions || typeof objs[key] != "function"){
12947                     this.add(key, objs[key]);
12948                 }
12949             }
12950         }
12951     },
12952    
12953 /**
12954  * Executes the specified function once for every item in the collection, passing each
12955  * item as the first and only parameter. returning false from the function will stop the iteration.
12956  * @param {Function} fn The function to execute for each item.
12957  * @param {Object} scope (optional) The scope in which to execute the function.
12958  */
12959     each : function(fn, scope){
12960         var items = [].concat(this.items); // each safe for removal
12961         for(var i = 0, len = items.length; i < len; i++){
12962             if(fn.call(scope || items[i], items[i], i, len) === false){
12963                 break;
12964             }
12965         }
12966     },
12967    
12968 /**
12969  * Executes the specified function once for every key in the collection, passing each
12970  * key, and its associated item as the first two parameters.
12971  * @param {Function} fn The function to execute for each item.
12972  * @param {Object} scope (optional) The scope in which to execute the function.
12973  */
12974     eachKey : function(fn, scope){
12975         for(var i = 0, len = this.keys.length; i < len; i++){
12976             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12977         }
12978     },
12979    
12980 /**
12981  * Returns the first item in the collection which elicits a true return value from the
12982  * passed selection function.
12983  * @param {Function} fn The selection function to execute for each item.
12984  * @param {Object} scope (optional) The scope in which to execute the function.
12985  * @return {Object} The first item in the collection which returned true from the selection function.
12986  */
12987     find : function(fn, scope){
12988         for(var i = 0, len = this.items.length; i < len; i++){
12989             if(fn.call(scope || window, this.items[i], this.keys[i])){
12990                 return this.items[i];
12991             }
12992         }
12993         return null;
12994     },
12995    
12996 /**
12997  * Inserts an item at the specified index in the collection.
12998  * @param {Number} index The index to insert the item at.
12999  * @param {String} key The key to associate with the new item, or the item itself.
13000  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13001  * @return {Object} The item inserted.
13002  */
13003     insert : function(index, key, o){
13004         if(arguments.length == 2){
13005             o = arguments[1];
13006             key = this.getKey(o);
13007         }
13008         if(index >= this.length){
13009             return this.add(key, o);
13010         }
13011         this.length++;
13012         this.items.splice(index, 0, o);
13013         if(typeof key != "undefined" && key != null){
13014             this.map[key] = o;
13015         }
13016         this.keys.splice(index, 0, key);
13017         this.fireEvent("add", index, o, key);
13018         return o;
13019     },
13020    
13021 /**
13022  * Removed an item from the collection.
13023  * @param {Object} o The item to remove.
13024  * @return {Object} The item removed.
13025  */
13026     remove : function(o){
13027         return this.removeAt(this.indexOf(o));
13028     },
13029    
13030 /**
13031  * Remove an item from a specified index in the collection.
13032  * @param {Number} index The index within the collection of the item to remove.
13033  */
13034     removeAt : function(index){
13035         if(index < this.length && index >= 0){
13036             this.length--;
13037             var o = this.items[index];
13038             this.items.splice(index, 1);
13039             var key = this.keys[index];
13040             if(typeof key != "undefined"){
13041                 delete this.map[key];
13042             }
13043             this.keys.splice(index, 1);
13044             this.fireEvent("remove", o, key);
13045         }
13046     },
13047    
13048 /**
13049  * Removed an item associated with the passed key fom the collection.
13050  * @param {String} key The key of the item to remove.
13051  */
13052     removeKey : function(key){
13053         return this.removeAt(this.indexOfKey(key));
13054     },
13055    
13056 /**
13057  * Returns the number of items in the collection.
13058  * @return {Number} the number of items in the collection.
13059  */
13060     getCount : function(){
13061         return this.length; 
13062     },
13063    
13064 /**
13065  * Returns index within the collection of the passed Object.
13066  * @param {Object} o The item to find the index of.
13067  * @return {Number} index of the item.
13068  */
13069     indexOf : function(o){
13070         if(!this.items.indexOf){
13071             for(var i = 0, len = this.items.length; i < len; i++){
13072                 if(this.items[i] == o) return i;
13073             }
13074             return -1;
13075         }else{
13076             return this.items.indexOf(o);
13077         }
13078     },
13079    
13080 /**
13081  * Returns index within the collection of the passed key.
13082  * @param {String} key The key to find the index of.
13083  * @return {Number} index of the key.
13084  */
13085     indexOfKey : function(key){
13086         if(!this.keys.indexOf){
13087             for(var i = 0, len = this.keys.length; i < len; i++){
13088                 if(this.keys[i] == key) return i;
13089             }
13090             return -1;
13091         }else{
13092             return this.keys.indexOf(key);
13093         }
13094     },
13095    
13096 /**
13097  * Returns the item associated with the passed key OR index. Key has priority over index.
13098  * @param {String/Number} key The key or index of the item.
13099  * @return {Object} The item associated with the passed key.
13100  */
13101     item : function(key){
13102         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13103         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13104     },
13105     
13106 /**
13107  * Returns the item at the specified index.
13108  * @param {Number} index The index of the item.
13109  * @return {Object}
13110  */
13111     itemAt : function(index){
13112         return this.items[index];
13113     },
13114     
13115 /**
13116  * Returns the item associated with the passed key.
13117  * @param {String/Number} key The key of the item.
13118  * @return {Object} The item associated with the passed key.
13119  */
13120     key : function(key){
13121         return this.map[key];
13122     },
13123    
13124 /**
13125  * Returns true if the collection contains the passed Object as an item.
13126  * @param {Object} o  The Object to look for in the collection.
13127  * @return {Boolean} True if the collection contains the Object as an item.
13128  */
13129     contains : function(o){
13130         return this.indexOf(o) != -1;
13131     },
13132    
13133 /**
13134  * Returns true if the collection contains the passed Object as a key.
13135  * @param {String} key The key to look for in the collection.
13136  * @return {Boolean} True if the collection contains the Object as a key.
13137  */
13138     containsKey : function(key){
13139         return typeof this.map[key] != "undefined";
13140     },
13141    
13142 /**
13143  * Removes all items from the collection.
13144  */
13145     clear : function(){
13146         this.length = 0;
13147         this.items = [];
13148         this.keys = [];
13149         this.map = {};
13150         this.fireEvent("clear");
13151     },
13152    
13153 /**
13154  * Returns the first item in the collection.
13155  * @return {Object} the first item in the collection..
13156  */
13157     first : function(){
13158         return this.items[0]; 
13159     },
13160    
13161 /**
13162  * Returns the last item in the collection.
13163  * @return {Object} the last item in the collection..
13164  */
13165     last : function(){
13166         return this.items[this.length-1];   
13167     },
13168     
13169     _sort : function(property, dir, fn){
13170         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13171         fn = fn || function(a, b){
13172             return a-b;
13173         };
13174         var c = [], k = this.keys, items = this.items;
13175         for(var i = 0, len = items.length; i < len; i++){
13176             c[c.length] = {key: k[i], value: items[i], index: i};
13177         }
13178         c.sort(function(a, b){
13179             var v = fn(a[property], b[property]) * dsc;
13180             if(v == 0){
13181                 v = (a.index < b.index ? -1 : 1);
13182             }
13183             return v;
13184         });
13185         for(var i = 0, len = c.length; i < len; i++){
13186             items[i] = c[i].value;
13187             k[i] = c[i].key;
13188         }
13189         this.fireEvent("sort", this);
13190     },
13191     
13192     /**
13193      * Sorts this collection with the passed comparison function
13194      * @param {String} direction (optional) "ASC" or "DESC"
13195      * @param {Function} fn (optional) comparison function
13196      */
13197     sort : function(dir, fn){
13198         this._sort("value", dir, fn);
13199     },
13200     
13201     /**
13202      * Sorts this collection by keys
13203      * @param {String} direction (optional) "ASC" or "DESC"
13204      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13205      */
13206     keySort : function(dir, fn){
13207         this._sort("key", dir, fn || function(a, b){
13208             return String(a).toUpperCase()-String(b).toUpperCase();
13209         });
13210     },
13211     
13212     /**
13213      * Returns a range of items in this collection
13214      * @param {Number} startIndex (optional) defaults to 0
13215      * @param {Number} endIndex (optional) default to the last item
13216      * @return {Array} An array of items
13217      */
13218     getRange : function(start, end){
13219         var items = this.items;
13220         if(items.length < 1){
13221             return [];
13222         }
13223         start = start || 0;
13224         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13225         var r = [];
13226         if(start <= end){
13227             for(var i = start; i <= end; i++) {
13228                     r[r.length] = items[i];
13229             }
13230         }else{
13231             for(var i = start; i >= end; i--) {
13232                     r[r.length] = items[i];
13233             }
13234         }
13235         return r;
13236     },
13237         
13238     /**
13239      * Filter the <i>objects</i> in this collection by a specific property. 
13240      * Returns a new collection that has been filtered.
13241      * @param {String} property A property on your objects
13242      * @param {String/RegExp} value Either string that the property values 
13243      * should start with or a RegExp to test against the property
13244      * @return {MixedCollection} The new filtered collection
13245      */
13246     filter : function(property, value){
13247         if(!value.exec){ // not a regex
13248             value = String(value);
13249             if(value.length == 0){
13250                 return this.clone();
13251             }
13252             value = new RegExp("^" + Roo.escapeRe(value), "i");
13253         }
13254         return this.filterBy(function(o){
13255             return o && value.test(o[property]);
13256         });
13257         },
13258     
13259     /**
13260      * Filter by a function. * Returns a new collection that has been filtered.
13261      * The passed function will be called with each 
13262      * object in the collection. If the function returns true, the value is included 
13263      * otherwise it is filtered.
13264      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13265      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13266      * @return {MixedCollection} The new filtered collection
13267      */
13268     filterBy : function(fn, scope){
13269         var r = new Roo.util.MixedCollection();
13270         r.getKey = this.getKey;
13271         var k = this.keys, it = this.items;
13272         for(var i = 0, len = it.length; i < len; i++){
13273             if(fn.call(scope||this, it[i], k[i])){
13274                                 r.add(k[i], it[i]);
13275                         }
13276         }
13277         return r;
13278     },
13279     
13280     /**
13281      * Creates a duplicate of this collection
13282      * @return {MixedCollection}
13283      */
13284     clone : function(){
13285         var r = new Roo.util.MixedCollection();
13286         var k = this.keys, it = this.items;
13287         for(var i = 0, len = it.length; i < len; i++){
13288             r.add(k[i], it[i]);
13289         }
13290         r.getKey = this.getKey;
13291         return r;
13292     }
13293 });
13294 /**
13295  * Returns the item associated with the passed key or index.
13296  * @method
13297  * @param {String/Number} key The key or index of the item.
13298  * @return {Object} The item associated with the passed key.
13299  */
13300 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13301  * Based on:
13302  * Ext JS Library 1.1.1
13303  * Copyright(c) 2006-2007, Ext JS, LLC.
13304  *
13305  * Originally Released Under LGPL - original licence link has changed is not relivant.
13306  *
13307  * Fork - LGPL
13308  * <script type="text/javascript">
13309  */
13310 /**
13311  * @class Roo.util.JSON
13312  * Modified version of Douglas Crockford"s json.js that doesn"t
13313  * mess with the Object prototype 
13314  * http://www.json.org/js.html
13315  * @singleton
13316  */
13317 Roo.util.JSON = new (function(){
13318     var useHasOwn = {}.hasOwnProperty ? true : false;
13319     
13320     // crashes Safari in some instances
13321     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13322     
13323     var pad = function(n) {
13324         return n < 10 ? "0" + n : n;
13325     };
13326     
13327     var m = {
13328         "\b": '\\b',
13329         "\t": '\\t',
13330         "\n": '\\n',
13331         "\f": '\\f',
13332         "\r": '\\r',
13333         '"' : '\\"',
13334         "\\": '\\\\'
13335     };
13336
13337     var encodeString = function(s){
13338         if (/["\\\x00-\x1f]/.test(s)) {
13339             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13340                 var c = m[b];
13341                 if(c){
13342                     return c;
13343                 }
13344                 c = b.charCodeAt();
13345                 return "\\u00" +
13346                     Math.floor(c / 16).toString(16) +
13347                     (c % 16).toString(16);
13348             }) + '"';
13349         }
13350         return '"' + s + '"';
13351     };
13352     
13353     var encodeArray = function(o){
13354         var a = ["["], b, i, l = o.length, v;
13355             for (i = 0; i < l; i += 1) {
13356                 v = o[i];
13357                 switch (typeof v) {
13358                     case "undefined":
13359                     case "function":
13360                     case "unknown":
13361                         break;
13362                     default:
13363                         if (b) {
13364                             a.push(',');
13365                         }
13366                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13367                         b = true;
13368                 }
13369             }
13370             a.push("]");
13371             return a.join("");
13372     };
13373     
13374     var encodeDate = function(o){
13375         return '"' + o.getFullYear() + "-" +
13376                 pad(o.getMonth() + 1) + "-" +
13377                 pad(o.getDate()) + "T" +
13378                 pad(o.getHours()) + ":" +
13379                 pad(o.getMinutes()) + ":" +
13380                 pad(o.getSeconds()) + '"';
13381     };
13382     
13383     /**
13384      * Encodes an Object, Array or other value
13385      * @param {Mixed} o The variable to encode
13386      * @return {String} The JSON string
13387      */
13388     this.encode = function(o)
13389     {
13390         // should this be extended to fully wrap stringify..
13391         
13392         if(typeof o == "undefined" || o === null){
13393             return "null";
13394         }else if(o instanceof Array){
13395             return encodeArray(o);
13396         }else if(o instanceof Date){
13397             return encodeDate(o);
13398         }else if(typeof o == "string"){
13399             return encodeString(o);
13400         }else if(typeof o == "number"){
13401             return isFinite(o) ? String(o) : "null";
13402         }else if(typeof o == "boolean"){
13403             return String(o);
13404         }else {
13405             var a = ["{"], b, i, v;
13406             for (i in o) {
13407                 if(!useHasOwn || o.hasOwnProperty(i)) {
13408                     v = o[i];
13409                     switch (typeof v) {
13410                     case "undefined":
13411                     case "function":
13412                     case "unknown":
13413                         break;
13414                     default:
13415                         if(b){
13416                             a.push(',');
13417                         }
13418                         a.push(this.encode(i), ":",
13419                                 v === null ? "null" : this.encode(v));
13420                         b = true;
13421                     }
13422                 }
13423             }
13424             a.push("}");
13425             return a.join("");
13426         }
13427     };
13428     
13429     /**
13430      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13431      * @param {String} json The JSON string
13432      * @return {Object} The resulting object
13433      */
13434     this.decode = function(json){
13435         
13436         return  /** eval:var:json */ eval("(" + json + ')');
13437     };
13438 })();
13439 /** 
13440  * Shorthand for {@link Roo.util.JSON#encode}
13441  * @member Roo encode 
13442  * @method */
13443 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13444 /** 
13445  * Shorthand for {@link Roo.util.JSON#decode}
13446  * @member Roo decode 
13447  * @method */
13448 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13449 /*
13450  * Based on:
13451  * Ext JS Library 1.1.1
13452  * Copyright(c) 2006-2007, Ext JS, LLC.
13453  *
13454  * Originally Released Under LGPL - original licence link has changed is not relivant.
13455  *
13456  * Fork - LGPL
13457  * <script type="text/javascript">
13458  */
13459  
13460 /**
13461  * @class Roo.util.Format
13462  * Reusable data formatting functions
13463  * @singleton
13464  */
13465 Roo.util.Format = function(){
13466     var trimRe = /^\s+|\s+$/g;
13467     return {
13468         /**
13469          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13470          * @param {String} value The string to truncate
13471          * @param {Number} length The maximum length to allow before truncating
13472          * @return {String} The converted text
13473          */
13474         ellipsis : function(value, len){
13475             if(value && value.length > len){
13476                 return value.substr(0, len-3)+"...";
13477             }
13478             return value;
13479         },
13480
13481         /**
13482          * Checks a reference and converts it to empty string if it is undefined
13483          * @param {Mixed} value Reference to check
13484          * @return {Mixed} Empty string if converted, otherwise the original value
13485          */
13486         undef : function(value){
13487             return typeof value != "undefined" ? value : "";
13488         },
13489
13490         /**
13491          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13492          * @param {String} value The string to encode
13493          * @return {String} The encoded text
13494          */
13495         htmlEncode : function(value){
13496             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13497         },
13498
13499         /**
13500          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13501          * @param {String} value The string to decode
13502          * @return {String} The decoded text
13503          */
13504         htmlDecode : function(value){
13505             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13506         },
13507
13508         /**
13509          * Trims any whitespace from either side of a string
13510          * @param {String} value The text to trim
13511          * @return {String} The trimmed text
13512          */
13513         trim : function(value){
13514             return String(value).replace(trimRe, "");
13515         },
13516
13517         /**
13518          * Returns a substring from within an original string
13519          * @param {String} value The original text
13520          * @param {Number} start The start index of the substring
13521          * @param {Number} length The length of the substring
13522          * @return {String} The substring
13523          */
13524         substr : function(value, start, length){
13525             return String(value).substr(start, length);
13526         },
13527
13528         /**
13529          * Converts a string to all lower case letters
13530          * @param {String} value The text to convert
13531          * @return {String} The converted text
13532          */
13533         lowercase : function(value){
13534             return String(value).toLowerCase();
13535         },
13536
13537         /**
13538          * Converts a string to all upper case letters
13539          * @param {String} value The text to convert
13540          * @return {String} The converted text
13541          */
13542         uppercase : function(value){
13543             return String(value).toUpperCase();
13544         },
13545
13546         /**
13547          * Converts the first character only of a string to upper case
13548          * @param {String} value The text to convert
13549          * @return {String} The converted text
13550          */
13551         capitalize : function(value){
13552             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13553         },
13554
13555         // private
13556         call : function(value, fn){
13557             if(arguments.length > 2){
13558                 var args = Array.prototype.slice.call(arguments, 2);
13559                 args.unshift(value);
13560                  
13561                 return /** eval:var:value */  eval(fn).apply(window, args);
13562             }else{
13563                 /** eval:var:value */
13564                 return /** eval:var:value */ eval(fn).call(window, value);
13565             }
13566         },
13567
13568        
13569         /**
13570          * safer version of Math.toFixed..??/
13571          * @param {Number/String} value The numeric value to format
13572          * @param {Number/String} value Decimal places 
13573          * @return {String} The formatted currency string
13574          */
13575         toFixed : function(v, n)
13576         {
13577             // why not use to fixed - precision is buggered???
13578             if (!n) {
13579                 return Math.round(v-0);
13580             }
13581             var fact = Math.pow(10,n+1);
13582             v = (Math.round((v-0)*fact))/fact;
13583             var z = (''+fact).substring(2);
13584             if (v == Math.floor(v)) {
13585                 return Math.floor(v) + '.' + z;
13586             }
13587             
13588             // now just padd decimals..
13589             var ps = String(v).split('.');
13590             var fd = (ps[1] + z);
13591             var r = fd.substring(0,n); 
13592             var rm = fd.substring(n); 
13593             if (rm < 5) {
13594                 return ps[0] + '.' + r;
13595             }
13596             r*=1; // turn it into a number;
13597             r++;
13598             if (String(r).length != n) {
13599                 ps[0]*=1;
13600                 ps[0]++;
13601                 r = String(r).substring(1); // chop the end off.
13602             }
13603             
13604             return ps[0] + '.' + r;
13605              
13606         },
13607         
13608         /**
13609          * Format a number as US currency
13610          * @param {Number/String} value The numeric value to format
13611          * @return {String} The formatted currency string
13612          */
13613         usMoney : function(v){
13614             return '$' + Roo.util.Format.number(v);
13615         },
13616         
13617         /**
13618          * Format a number
13619          * eventually this should probably emulate php's number_format
13620          * @param {Number/String} value The numeric value to format
13621          * @param {Number} decimals number of decimal places
13622          * @return {String} The formatted currency string
13623          */
13624         number : function(v,decimals)
13625         {
13626             // multiply and round.
13627             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13628             var mul = Math.pow(10, decimals);
13629             var zero = String(mul).substring(1);
13630             v = (Math.round((v-0)*mul))/mul;
13631             
13632             // if it's '0' number.. then
13633             
13634             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13635             v = String(v);
13636             var ps = v.split('.');
13637             var whole = ps[0];
13638             
13639             
13640             var r = /(\d+)(\d{3})/;
13641             // add comma's
13642             while (r.test(whole)) {
13643                 whole = whole.replace(r, '$1' + ',' + '$2');
13644             }
13645             
13646             
13647             var sub = ps[1] ?
13648                     // has decimals..
13649                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13650                     // does not have decimals
13651                     (decimals ? ('.' + zero) : '');
13652             
13653             
13654             return whole + sub ;
13655         },
13656         
13657         /**
13658          * Parse a value into a formatted date using the specified format pattern.
13659          * @param {Mixed} value The value to format
13660          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13661          * @return {String} The formatted date string
13662          */
13663         date : function(v, format){
13664             if(!v){
13665                 return "";
13666             }
13667             if(!(v instanceof Date)){
13668                 v = new Date(Date.parse(v));
13669             }
13670             return v.dateFormat(format || Roo.util.Format.defaults.date);
13671         },
13672
13673         /**
13674          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13675          * @param {String} format Any valid date format string
13676          * @return {Function} The date formatting function
13677          */
13678         dateRenderer : function(format){
13679             return function(v){
13680                 return Roo.util.Format.date(v, format);  
13681             };
13682         },
13683
13684         // private
13685         stripTagsRE : /<\/?[^>]+>/gi,
13686         
13687         /**
13688          * Strips all HTML tags
13689          * @param {Mixed} value The text from which to strip tags
13690          * @return {String} The stripped text
13691          */
13692         stripTags : function(v){
13693             return !v ? v : String(v).replace(this.stripTagsRE, "");
13694         }
13695     };
13696 }();
13697 Roo.util.Format.defaults = {
13698     date : 'd/M/Y'
13699 };/*
13700  * Based on:
13701  * Ext JS Library 1.1.1
13702  * Copyright(c) 2006-2007, Ext JS, LLC.
13703  *
13704  * Originally Released Under LGPL - original licence link has changed is not relivant.
13705  *
13706  * Fork - LGPL
13707  * <script type="text/javascript">
13708  */
13709
13710
13711  
13712
13713 /**
13714  * @class Roo.MasterTemplate
13715  * @extends Roo.Template
13716  * Provides a template that can have child templates. The syntax is:
13717 <pre><code>
13718 var t = new Roo.MasterTemplate(
13719         '&lt;select name="{name}"&gt;',
13720                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13721         '&lt;/select&gt;'
13722 );
13723 t.add('options', {value: 'foo', text: 'bar'});
13724 // or you can add multiple child elements in one shot
13725 t.addAll('options', [
13726     {value: 'foo', text: 'bar'},
13727     {value: 'foo2', text: 'bar2'},
13728     {value: 'foo3', text: 'bar3'}
13729 ]);
13730 // then append, applying the master template values
13731 t.append('my-form', {name: 'my-select'});
13732 </code></pre>
13733 * A name attribute for the child template is not required if you have only one child
13734 * template or you want to refer to them by index.
13735  */
13736 Roo.MasterTemplate = function(){
13737     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13738     this.originalHtml = this.html;
13739     var st = {};
13740     var m, re = this.subTemplateRe;
13741     re.lastIndex = 0;
13742     var subIndex = 0;
13743     while(m = re.exec(this.html)){
13744         var name = m[1], content = m[2];
13745         st[subIndex] = {
13746             name: name,
13747             index: subIndex,
13748             buffer: [],
13749             tpl : new Roo.Template(content)
13750         };
13751         if(name){
13752             st[name] = st[subIndex];
13753         }
13754         st[subIndex].tpl.compile();
13755         st[subIndex].tpl.call = this.call.createDelegate(this);
13756         subIndex++;
13757     }
13758     this.subCount = subIndex;
13759     this.subs = st;
13760 };
13761 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13762     /**
13763     * The regular expression used to match sub templates
13764     * @type RegExp
13765     * @property
13766     */
13767     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13768
13769     /**
13770      * Applies the passed values to a child template.
13771      * @param {String/Number} name (optional) The name or index of the child template
13772      * @param {Array/Object} values The values to be applied to the template
13773      * @return {MasterTemplate} this
13774      */
13775      add : function(name, values){
13776         if(arguments.length == 1){
13777             values = arguments[0];
13778             name = 0;
13779         }
13780         var s = this.subs[name];
13781         s.buffer[s.buffer.length] = s.tpl.apply(values);
13782         return this;
13783     },
13784
13785     /**
13786      * Applies all the passed values to a child template.
13787      * @param {String/Number} name (optional) The name or index of the child template
13788      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13789      * @param {Boolean} reset (optional) True to reset the template first
13790      * @return {MasterTemplate} this
13791      */
13792     fill : function(name, values, reset){
13793         var a = arguments;
13794         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13795             values = a[0];
13796             name = 0;
13797             reset = a[1];
13798         }
13799         if(reset){
13800             this.reset();
13801         }
13802         for(var i = 0, len = values.length; i < len; i++){
13803             this.add(name, values[i]);
13804         }
13805         return this;
13806     },
13807
13808     /**
13809      * Resets the template for reuse
13810      * @return {MasterTemplate} this
13811      */
13812      reset : function(){
13813         var s = this.subs;
13814         for(var i = 0; i < this.subCount; i++){
13815             s[i].buffer = [];
13816         }
13817         return this;
13818     },
13819
13820     applyTemplate : function(values){
13821         var s = this.subs;
13822         var replaceIndex = -1;
13823         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13824             return s[++replaceIndex].buffer.join("");
13825         });
13826         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13827     },
13828
13829     apply : function(){
13830         return this.applyTemplate.apply(this, arguments);
13831     },
13832
13833     compile : function(){return this;}
13834 });
13835
13836 /**
13837  * Alias for fill().
13838  * @method
13839  */
13840 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13841  /**
13842  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13843  * var tpl = Roo.MasterTemplate.from('element-id');
13844  * @param {String/HTMLElement} el
13845  * @param {Object} config
13846  * @static
13847  */
13848 Roo.MasterTemplate.from = function(el, config){
13849     el = Roo.getDom(el);
13850     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13851 };/*
13852  * Based on:
13853  * Ext JS Library 1.1.1
13854  * Copyright(c) 2006-2007, Ext JS, LLC.
13855  *
13856  * Originally Released Under LGPL - original licence link has changed is not relivant.
13857  *
13858  * Fork - LGPL
13859  * <script type="text/javascript">
13860  */
13861
13862  
13863 /**
13864  * @class Roo.util.CSS
13865  * Utility class for manipulating CSS rules
13866  * @singleton
13867  */
13868 Roo.util.CSS = function(){
13869         var rules = null;
13870         var doc = document;
13871
13872     var camelRe = /(-[a-z])/gi;
13873     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13874
13875    return {
13876    /**
13877     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13878     * tag and appended to the HEAD of the document.
13879     * @param {String|Object} cssText The text containing the css rules
13880     * @param {String} id An id to add to the stylesheet for later removal
13881     * @return {StyleSheet}
13882     */
13883     createStyleSheet : function(cssText, id){
13884         var ss;
13885         var head = doc.getElementsByTagName("head")[0];
13886         var nrules = doc.createElement("style");
13887         nrules.setAttribute("type", "text/css");
13888         if(id){
13889             nrules.setAttribute("id", id);
13890         }
13891         if (typeof(cssText) != 'string') {
13892             // support object maps..
13893             // not sure if this a good idea.. 
13894             // perhaps it should be merged with the general css handling
13895             // and handle js style props.
13896             var cssTextNew = [];
13897             for(var n in cssText) {
13898                 var citems = [];
13899                 for(var k in cssText[n]) {
13900                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13901                 }
13902                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13903                 
13904             }
13905             cssText = cssTextNew.join("\n");
13906             
13907         }
13908        
13909        
13910        if(Roo.isIE){
13911            head.appendChild(nrules);
13912            ss = nrules.styleSheet;
13913            ss.cssText = cssText;
13914        }else{
13915            try{
13916                 nrules.appendChild(doc.createTextNode(cssText));
13917            }catch(e){
13918                nrules.cssText = cssText; 
13919            }
13920            head.appendChild(nrules);
13921            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13922        }
13923        this.cacheStyleSheet(ss);
13924        return ss;
13925    },
13926
13927    /**
13928     * Removes a style or link tag by id
13929     * @param {String} id The id of the tag
13930     */
13931    removeStyleSheet : function(id){
13932        var existing = doc.getElementById(id);
13933        if(existing){
13934            existing.parentNode.removeChild(existing);
13935        }
13936    },
13937
13938    /**
13939     * Dynamically swaps an existing stylesheet reference for a new one
13940     * @param {String} id The id of an existing link tag to remove
13941     * @param {String} url The href of the new stylesheet to include
13942     */
13943    swapStyleSheet : function(id, url){
13944        this.removeStyleSheet(id);
13945        var ss = doc.createElement("link");
13946        ss.setAttribute("rel", "stylesheet");
13947        ss.setAttribute("type", "text/css");
13948        ss.setAttribute("id", id);
13949        ss.setAttribute("href", url);
13950        doc.getElementsByTagName("head")[0].appendChild(ss);
13951    },
13952    
13953    /**
13954     * Refresh the rule cache if you have dynamically added stylesheets
13955     * @return {Object} An object (hash) of rules indexed by selector
13956     */
13957    refreshCache : function(){
13958        return this.getRules(true);
13959    },
13960
13961    // private
13962    cacheStyleSheet : function(stylesheet){
13963        if(!rules){
13964            rules = {};
13965        }
13966        try{// try catch for cross domain access issue
13967            var ssRules = stylesheet.cssRules || stylesheet.rules;
13968            for(var j = ssRules.length-1; j >= 0; --j){
13969                rules[ssRules[j].selectorText] = ssRules[j];
13970            }
13971        }catch(e){}
13972    },
13973    
13974    /**
13975     * Gets all css rules for the document
13976     * @param {Boolean} refreshCache true to refresh the internal cache
13977     * @return {Object} An object (hash) of rules indexed by selector
13978     */
13979    getRules : function(refreshCache){
13980                 if(rules == null || refreshCache){
13981                         rules = {};
13982                         var ds = doc.styleSheets;
13983                         for(var i =0, len = ds.length; i < len; i++){
13984                             try{
13985                         this.cacheStyleSheet(ds[i]);
13986                     }catch(e){} 
13987                 }
13988                 }
13989                 return rules;
13990         },
13991         
13992         /**
13993     * Gets an an individual CSS rule by selector(s)
13994     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13995     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13996     * @return {CSSRule} The CSS rule or null if one is not found
13997     */
13998    getRule : function(selector, refreshCache){
13999                 var rs = this.getRules(refreshCache);
14000                 if(!(selector instanceof Array)){
14001                     return rs[selector];
14002                 }
14003                 for(var i = 0; i < selector.length; i++){
14004                         if(rs[selector[i]]){
14005                                 return rs[selector[i]];
14006                         }
14007                 }
14008                 return null;
14009         },
14010         
14011         
14012         /**
14013     * Updates a rule property
14014     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14015     * @param {String} property The css property
14016     * @param {String} value The new value for the property
14017     * @return {Boolean} true If a rule was found and updated
14018     */
14019    updateRule : function(selector, property, value){
14020                 if(!(selector instanceof Array)){
14021                         var rule = this.getRule(selector);
14022                         if(rule){
14023                                 rule.style[property.replace(camelRe, camelFn)] = value;
14024                                 return true;
14025                         }
14026                 }else{
14027                         for(var i = 0; i < selector.length; i++){
14028                                 if(this.updateRule(selector[i], property, value)){
14029                                         return true;
14030                                 }
14031                         }
14032                 }
14033                 return false;
14034         }
14035    };   
14036 }();/*
14037  * Based on:
14038  * Ext JS Library 1.1.1
14039  * Copyright(c) 2006-2007, Ext JS, LLC.
14040  *
14041  * Originally Released Under LGPL - original licence link has changed is not relivant.
14042  *
14043  * Fork - LGPL
14044  * <script type="text/javascript">
14045  */
14046
14047  
14048
14049 /**
14050  * @class Roo.util.ClickRepeater
14051  * @extends Roo.util.Observable
14052  * 
14053  * A wrapper class which can be applied to any element. Fires a "click" event while the
14054  * mouse is pressed. The interval between firings may be specified in the config but
14055  * defaults to 10 milliseconds.
14056  * 
14057  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14058  * 
14059  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14060  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14061  * Similar to an autorepeat key delay.
14062  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14063  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14064  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14065  *           "interval" and "delay" are ignored. "immediate" is honored.
14066  * @cfg {Boolean} preventDefault True to prevent the default click event
14067  * @cfg {Boolean} stopDefault True to stop the default click event
14068  * 
14069  * @history
14070  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14071  *     2007-02-02 jvs Renamed to ClickRepeater
14072  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14073  *
14074  *  @constructor
14075  * @param {String/HTMLElement/Element} el The element to listen on
14076  * @param {Object} config
14077  **/
14078 Roo.util.ClickRepeater = function(el, config)
14079 {
14080     this.el = Roo.get(el);
14081     this.el.unselectable();
14082
14083     Roo.apply(this, config);
14084
14085     this.addEvents({
14086     /**
14087      * @event mousedown
14088      * Fires when the mouse button is depressed.
14089      * @param {Roo.util.ClickRepeater} this
14090      */
14091         "mousedown" : true,
14092     /**
14093      * @event click
14094      * Fires on a specified interval during the time the element is pressed.
14095      * @param {Roo.util.ClickRepeater} this
14096      */
14097         "click" : true,
14098     /**
14099      * @event mouseup
14100      * Fires when the mouse key is released.
14101      * @param {Roo.util.ClickRepeater} this
14102      */
14103         "mouseup" : true
14104     });
14105
14106     this.el.on("mousedown", this.handleMouseDown, this);
14107     if(this.preventDefault || this.stopDefault){
14108         this.el.on("click", function(e){
14109             if(this.preventDefault){
14110                 e.preventDefault();
14111             }
14112             if(this.stopDefault){
14113                 e.stopEvent();
14114             }
14115         }, this);
14116     }
14117
14118     // allow inline handler
14119     if(this.handler){
14120         this.on("click", this.handler,  this.scope || this);
14121     }
14122
14123     Roo.util.ClickRepeater.superclass.constructor.call(this);
14124 };
14125
14126 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14127     interval : 20,
14128     delay: 250,
14129     preventDefault : true,
14130     stopDefault : false,
14131     timer : 0,
14132
14133     // private
14134     handleMouseDown : function(){
14135         clearTimeout(this.timer);
14136         this.el.blur();
14137         if(this.pressClass){
14138             this.el.addClass(this.pressClass);
14139         }
14140         this.mousedownTime = new Date();
14141
14142         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14143         this.el.on("mouseout", this.handleMouseOut, this);
14144
14145         this.fireEvent("mousedown", this);
14146         this.fireEvent("click", this);
14147         
14148         this.timer = this.click.defer(this.delay || this.interval, this);
14149     },
14150
14151     // private
14152     click : function(){
14153         this.fireEvent("click", this);
14154         this.timer = this.click.defer(this.getInterval(), this);
14155     },
14156
14157     // private
14158     getInterval: function(){
14159         if(!this.accelerate){
14160             return this.interval;
14161         }
14162         var pressTime = this.mousedownTime.getElapsed();
14163         if(pressTime < 500){
14164             return 400;
14165         }else if(pressTime < 1700){
14166             return 320;
14167         }else if(pressTime < 2600){
14168             return 250;
14169         }else if(pressTime < 3500){
14170             return 180;
14171         }else if(pressTime < 4400){
14172             return 140;
14173         }else if(pressTime < 5300){
14174             return 80;
14175         }else if(pressTime < 6200){
14176             return 50;
14177         }else{
14178             return 10;
14179         }
14180     },
14181
14182     // private
14183     handleMouseOut : function(){
14184         clearTimeout(this.timer);
14185         if(this.pressClass){
14186             this.el.removeClass(this.pressClass);
14187         }
14188         this.el.on("mouseover", this.handleMouseReturn, this);
14189     },
14190
14191     // private
14192     handleMouseReturn : function(){
14193         this.el.un("mouseover", this.handleMouseReturn);
14194         if(this.pressClass){
14195             this.el.addClass(this.pressClass);
14196         }
14197         this.click();
14198     },
14199
14200     // private
14201     handleMouseUp : function(){
14202         clearTimeout(this.timer);
14203         this.el.un("mouseover", this.handleMouseReturn);
14204         this.el.un("mouseout", this.handleMouseOut);
14205         Roo.get(document).un("mouseup", this.handleMouseUp);
14206         this.el.removeClass(this.pressClass);
14207         this.fireEvent("mouseup", this);
14208     }
14209 });/*
14210  * Based on:
14211  * Ext JS Library 1.1.1
14212  * Copyright(c) 2006-2007, Ext JS, LLC.
14213  *
14214  * Originally Released Under LGPL - original licence link has changed is not relivant.
14215  *
14216  * Fork - LGPL
14217  * <script type="text/javascript">
14218  */
14219
14220  
14221 /**
14222  * @class Roo.KeyNav
14223  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14224  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14225  * way to implement custom navigation schemes for any UI component.</p>
14226  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14227  * pageUp, pageDown, del, home, end.  Usage:</p>
14228  <pre><code>
14229 var nav = new Roo.KeyNav("my-element", {
14230     "left" : function(e){
14231         this.moveLeft(e.ctrlKey);
14232     },
14233     "right" : function(e){
14234         this.moveRight(e.ctrlKey);
14235     },
14236     "enter" : function(e){
14237         this.save();
14238     },
14239     scope : this
14240 });
14241 </code></pre>
14242  * @constructor
14243  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14244  * @param {Object} config The config
14245  */
14246 Roo.KeyNav = function(el, config){
14247     this.el = Roo.get(el);
14248     Roo.apply(this, config);
14249     if(!this.disabled){
14250         this.disabled = true;
14251         this.enable();
14252     }
14253 };
14254
14255 Roo.KeyNav.prototype = {
14256     /**
14257      * @cfg {Boolean} disabled
14258      * True to disable this KeyNav instance (defaults to false)
14259      */
14260     disabled : false,
14261     /**
14262      * @cfg {String} defaultEventAction
14263      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14264      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14265      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14266      */
14267     defaultEventAction: "stopEvent",
14268     /**
14269      * @cfg {Boolean} forceKeyDown
14270      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14271      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14272      * handle keydown instead of keypress.
14273      */
14274     forceKeyDown : false,
14275
14276     // private
14277     prepareEvent : function(e){
14278         var k = e.getKey();
14279         var h = this.keyToHandler[k];
14280         //if(h && this[h]){
14281         //    e.stopPropagation();
14282         //}
14283         if(Roo.isSafari && h && k >= 37 && k <= 40){
14284             e.stopEvent();
14285         }
14286     },
14287
14288     // private
14289     relay : function(e){
14290         var k = e.getKey();
14291         var h = this.keyToHandler[k];
14292         if(h && this[h]){
14293             if(this.doRelay(e, this[h], h) !== true){
14294                 e[this.defaultEventAction]();
14295             }
14296         }
14297     },
14298
14299     // private
14300     doRelay : function(e, h, hname){
14301         return h.call(this.scope || this, e);
14302     },
14303
14304     // possible handlers
14305     enter : false,
14306     left : false,
14307     right : false,
14308     up : false,
14309     down : false,
14310     tab : false,
14311     esc : false,
14312     pageUp : false,
14313     pageDown : false,
14314     del : false,
14315     home : false,
14316     end : false,
14317
14318     // quick lookup hash
14319     keyToHandler : {
14320         37 : "left",
14321         39 : "right",
14322         38 : "up",
14323         40 : "down",
14324         33 : "pageUp",
14325         34 : "pageDown",
14326         46 : "del",
14327         36 : "home",
14328         35 : "end",
14329         13 : "enter",
14330         27 : "esc",
14331         9  : "tab"
14332     },
14333
14334         /**
14335          * Enable this KeyNav
14336          */
14337         enable: function(){
14338                 if(this.disabled){
14339             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14340             // the EventObject will normalize Safari automatically
14341             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14342                 this.el.on("keydown", this.relay,  this);
14343             }else{
14344                 this.el.on("keydown", this.prepareEvent,  this);
14345                 this.el.on("keypress", this.relay,  this);
14346             }
14347                     this.disabled = false;
14348                 }
14349         },
14350
14351         /**
14352          * Disable this KeyNav
14353          */
14354         disable: function(){
14355                 if(!this.disabled){
14356                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14357                 this.el.un("keydown", this.relay);
14358             }else{
14359                 this.el.un("keydown", this.prepareEvent);
14360                 this.el.un("keypress", this.relay);
14361             }
14362                     this.disabled = true;
14363                 }
14364         }
14365 };/*
14366  * Based on:
14367  * Ext JS Library 1.1.1
14368  * Copyright(c) 2006-2007, Ext JS, LLC.
14369  *
14370  * Originally Released Under LGPL - original licence link has changed is not relivant.
14371  *
14372  * Fork - LGPL
14373  * <script type="text/javascript">
14374  */
14375
14376  
14377 /**
14378  * @class Roo.KeyMap
14379  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14380  * The constructor accepts the same config object as defined by {@link #addBinding}.
14381  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14382  * combination it will call the function with this signature (if the match is a multi-key
14383  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14384  * A KeyMap can also handle a string representation of keys.<br />
14385  * Usage:
14386  <pre><code>
14387 // map one key by key code
14388 var map = new Roo.KeyMap("my-element", {
14389     key: 13, // or Roo.EventObject.ENTER
14390     fn: myHandler,
14391     scope: myObject
14392 });
14393
14394 // map multiple keys to one action by string
14395 var map = new Roo.KeyMap("my-element", {
14396     key: "a\r\n\t",
14397     fn: myHandler,
14398     scope: myObject
14399 });
14400
14401 // map multiple keys to multiple actions by strings and array of codes
14402 var map = new Roo.KeyMap("my-element", [
14403     {
14404         key: [10,13],
14405         fn: function(){ alert("Return was pressed"); }
14406     }, {
14407         key: "abc",
14408         fn: function(){ alert('a, b or c was pressed'); }
14409     }, {
14410         key: "\t",
14411         ctrl:true,
14412         shift:true,
14413         fn: function(){ alert('Control + shift + tab was pressed.'); }
14414     }
14415 ]);
14416 </code></pre>
14417  * <b>Note: A KeyMap starts enabled</b>
14418  * @constructor
14419  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14420  * @param {Object} config The config (see {@link #addBinding})
14421  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14422  */
14423 Roo.KeyMap = function(el, config, eventName){
14424     this.el  = Roo.get(el);
14425     this.eventName = eventName || "keydown";
14426     this.bindings = [];
14427     if(config){
14428         this.addBinding(config);
14429     }
14430     this.enable();
14431 };
14432
14433 Roo.KeyMap.prototype = {
14434     /**
14435      * True to stop the event from bubbling and prevent the default browser action if the
14436      * key was handled by the KeyMap (defaults to false)
14437      * @type Boolean
14438      */
14439     stopEvent : false,
14440
14441     /**
14442      * Add a new binding to this KeyMap. The following config object properties are supported:
14443      * <pre>
14444 Property    Type             Description
14445 ----------  ---------------  ----------------------------------------------------------------------
14446 key         String/Array     A single keycode or an array of keycodes to handle
14447 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14448 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14449 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14450 fn          Function         The function to call when KeyMap finds the expected key combination
14451 scope       Object           The scope of the callback function
14452 </pre>
14453      *
14454      * Usage:
14455      * <pre><code>
14456 // Create a KeyMap
14457 var map = new Roo.KeyMap(document, {
14458     key: Roo.EventObject.ENTER,
14459     fn: handleKey,
14460     scope: this
14461 });
14462
14463 //Add a new binding to the existing KeyMap later
14464 map.addBinding({
14465     key: 'abc',
14466     shift: true,
14467     fn: handleKey,
14468     scope: this
14469 });
14470 </code></pre>
14471      * @param {Object/Array} config A single KeyMap config or an array of configs
14472      */
14473         addBinding : function(config){
14474         if(config instanceof Array){
14475             for(var i = 0, len = config.length; i < len; i++){
14476                 this.addBinding(config[i]);
14477             }
14478             return;
14479         }
14480         var keyCode = config.key,
14481             shift = config.shift, 
14482             ctrl = config.ctrl, 
14483             alt = config.alt,
14484             fn = config.fn,
14485             scope = config.scope;
14486         if(typeof keyCode == "string"){
14487             var ks = [];
14488             var keyString = keyCode.toUpperCase();
14489             for(var j = 0, len = keyString.length; j < len; j++){
14490                 ks.push(keyString.charCodeAt(j));
14491             }
14492             keyCode = ks;
14493         }
14494         var keyArray = keyCode instanceof Array;
14495         var handler = function(e){
14496             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14497                 var k = e.getKey();
14498                 if(keyArray){
14499                     for(var i = 0, len = keyCode.length; i < len; i++){
14500                         if(keyCode[i] == k){
14501                           if(this.stopEvent){
14502                               e.stopEvent();
14503                           }
14504                           fn.call(scope || window, k, e);
14505                           return;
14506                         }
14507                     }
14508                 }else{
14509                     if(k == keyCode){
14510                         if(this.stopEvent){
14511                            e.stopEvent();
14512                         }
14513                         fn.call(scope || window, k, e);
14514                     }
14515                 }
14516             }
14517         };
14518         this.bindings.push(handler);  
14519         },
14520
14521     /**
14522      * Shorthand for adding a single key listener
14523      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14524      * following options:
14525      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14526      * @param {Function} fn The function to call
14527      * @param {Object} scope (optional) The scope of the function
14528      */
14529     on : function(key, fn, scope){
14530         var keyCode, shift, ctrl, alt;
14531         if(typeof key == "object" && !(key instanceof Array)){
14532             keyCode = key.key;
14533             shift = key.shift;
14534             ctrl = key.ctrl;
14535             alt = key.alt;
14536         }else{
14537             keyCode = key;
14538         }
14539         this.addBinding({
14540             key: keyCode,
14541             shift: shift,
14542             ctrl: ctrl,
14543             alt: alt,
14544             fn: fn,
14545             scope: scope
14546         })
14547     },
14548
14549     // private
14550     handleKeyDown : function(e){
14551             if(this.enabled){ //just in case
14552             var b = this.bindings;
14553             for(var i = 0, len = b.length; i < len; i++){
14554                 b[i].call(this, e);
14555             }
14556             }
14557         },
14558         
14559         /**
14560          * Returns true if this KeyMap is enabled
14561          * @return {Boolean} 
14562          */
14563         isEnabled : function(){
14564             return this.enabled;  
14565         },
14566         
14567         /**
14568          * Enables this KeyMap
14569          */
14570         enable: function(){
14571                 if(!this.enabled){
14572                     this.el.on(this.eventName, this.handleKeyDown, this);
14573                     this.enabled = true;
14574                 }
14575         },
14576
14577         /**
14578          * Disable this KeyMap
14579          */
14580         disable: function(){
14581                 if(this.enabled){
14582                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14583                     this.enabled = false;
14584                 }
14585         }
14586 };/*
14587  * Based on:
14588  * Ext JS Library 1.1.1
14589  * Copyright(c) 2006-2007, Ext JS, LLC.
14590  *
14591  * Originally Released Under LGPL - original licence link has changed is not relivant.
14592  *
14593  * Fork - LGPL
14594  * <script type="text/javascript">
14595  */
14596
14597  
14598 /**
14599  * @class Roo.util.TextMetrics
14600  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14601  * wide, in pixels, a given block of text will be.
14602  * @singleton
14603  */
14604 Roo.util.TextMetrics = function(){
14605     var shared;
14606     return {
14607         /**
14608          * Measures the size of the specified text
14609          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14610          * that can affect the size of the rendered text
14611          * @param {String} text The text to measure
14612          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14613          * in order to accurately measure the text height
14614          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14615          */
14616         measure : function(el, text, fixedWidth){
14617             if(!shared){
14618                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14619             }
14620             shared.bind(el);
14621             shared.setFixedWidth(fixedWidth || 'auto');
14622             return shared.getSize(text);
14623         },
14624
14625         /**
14626          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14627          * the overhead of multiple calls to initialize the style properties on each measurement.
14628          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14629          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14630          * in order to accurately measure the text height
14631          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14632          */
14633         createInstance : function(el, fixedWidth){
14634             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14635         }
14636     };
14637 }();
14638
14639  
14640
14641 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14642     var ml = new Roo.Element(document.createElement('div'));
14643     document.body.appendChild(ml.dom);
14644     ml.position('absolute');
14645     ml.setLeftTop(-1000, -1000);
14646     ml.hide();
14647
14648     if(fixedWidth){
14649         ml.setWidth(fixedWidth);
14650     }
14651      
14652     var instance = {
14653         /**
14654          * Returns the size of the specified text based on the internal element's style and width properties
14655          * @memberOf Roo.util.TextMetrics.Instance#
14656          * @param {String} text The text to measure
14657          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14658          */
14659         getSize : function(text){
14660             ml.update(text);
14661             var s = ml.getSize();
14662             ml.update('');
14663             return s;
14664         },
14665
14666         /**
14667          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14668          * that can affect the size of the rendered text
14669          * @memberOf Roo.util.TextMetrics.Instance#
14670          * @param {String/HTMLElement} el The element, dom node or id
14671          */
14672         bind : function(el){
14673             ml.setStyle(
14674                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14675             );
14676         },
14677
14678         /**
14679          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14680          * to set a fixed width in order to accurately measure the text height.
14681          * @memberOf Roo.util.TextMetrics.Instance#
14682          * @param {Number} width The width to set on the element
14683          */
14684         setFixedWidth : function(width){
14685             ml.setWidth(width);
14686         },
14687
14688         /**
14689          * Returns the measured width of the specified text
14690          * @memberOf Roo.util.TextMetrics.Instance#
14691          * @param {String} text The text to measure
14692          * @return {Number} width The width in pixels
14693          */
14694         getWidth : function(text){
14695             ml.dom.style.width = 'auto';
14696             return this.getSize(text).width;
14697         },
14698
14699         /**
14700          * Returns the measured height of the specified text.  For multiline text, be sure to call
14701          * {@link #setFixedWidth} if necessary.
14702          * @memberOf Roo.util.TextMetrics.Instance#
14703          * @param {String} text The text to measure
14704          * @return {Number} height The height in pixels
14705          */
14706         getHeight : function(text){
14707             return this.getSize(text).height;
14708         }
14709     };
14710
14711     instance.bind(bindTo);
14712
14713     return instance;
14714 };
14715
14716 // backwards compat
14717 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14718  * Based on:
14719  * Ext JS Library 1.1.1
14720  * Copyright(c) 2006-2007, Ext JS, LLC.
14721  *
14722  * Originally Released Under LGPL - original licence link has changed is not relivant.
14723  *
14724  * Fork - LGPL
14725  * <script type="text/javascript">
14726  */
14727
14728 /**
14729  * @class Roo.state.Provider
14730  * Abstract base class for state provider implementations. This class provides methods
14731  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14732  * Provider interface.
14733  */
14734 Roo.state.Provider = function(){
14735     /**
14736      * @event statechange
14737      * Fires when a state change occurs.
14738      * @param {Provider} this This state provider
14739      * @param {String} key The state key which was changed
14740      * @param {String} value The encoded value for the state
14741      */
14742     this.addEvents({
14743         "statechange": true
14744     });
14745     this.state = {};
14746     Roo.state.Provider.superclass.constructor.call(this);
14747 };
14748 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14749     /**
14750      * Returns the current value for a key
14751      * @param {String} name The key name
14752      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14753      * @return {Mixed} The state data
14754      */
14755     get : function(name, defaultValue){
14756         return typeof this.state[name] == "undefined" ?
14757             defaultValue : this.state[name];
14758     },
14759     
14760     /**
14761      * Clears a value from the state
14762      * @param {String} name The key name
14763      */
14764     clear : function(name){
14765         delete this.state[name];
14766         this.fireEvent("statechange", this, name, null);
14767     },
14768     
14769     /**
14770      * Sets the value for a key
14771      * @param {String} name The key name
14772      * @param {Mixed} value The value to set
14773      */
14774     set : function(name, value){
14775         this.state[name] = value;
14776         this.fireEvent("statechange", this, name, value);
14777     },
14778     
14779     /**
14780      * Decodes a string previously encoded with {@link #encodeValue}.
14781      * @param {String} value The value to decode
14782      * @return {Mixed} The decoded value
14783      */
14784     decodeValue : function(cookie){
14785         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14786         var matches = re.exec(unescape(cookie));
14787         if(!matches || !matches[1]) return; // non state cookie
14788         var type = matches[1];
14789         var v = matches[2];
14790         switch(type){
14791             case "n":
14792                 return parseFloat(v);
14793             case "d":
14794                 return new Date(Date.parse(v));
14795             case "b":
14796                 return (v == "1");
14797             case "a":
14798                 var all = [];
14799                 var values = v.split("^");
14800                 for(var i = 0, len = values.length; i < len; i++){
14801                     all.push(this.decodeValue(values[i]));
14802                 }
14803                 return all;
14804            case "o":
14805                 var all = {};
14806                 var values = v.split("^");
14807                 for(var i = 0, len = values.length; i < len; i++){
14808                     var kv = values[i].split("=");
14809                     all[kv[0]] = this.decodeValue(kv[1]);
14810                 }
14811                 return all;
14812            default:
14813                 return v;
14814         }
14815     },
14816     
14817     /**
14818      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14819      * @param {Mixed} value The value to encode
14820      * @return {String} The encoded value
14821      */
14822     encodeValue : function(v){
14823         var enc;
14824         if(typeof v == "number"){
14825             enc = "n:" + v;
14826         }else if(typeof v == "boolean"){
14827             enc = "b:" + (v ? "1" : "0");
14828         }else if(v instanceof Date){
14829             enc = "d:" + v.toGMTString();
14830         }else if(v instanceof Array){
14831             var flat = "";
14832             for(var i = 0, len = v.length; i < len; i++){
14833                 flat += this.encodeValue(v[i]);
14834                 if(i != len-1) flat += "^";
14835             }
14836             enc = "a:" + flat;
14837         }else if(typeof v == "object"){
14838             var flat = "";
14839             for(var key in v){
14840                 if(typeof v[key] != "function"){
14841                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14842                 }
14843             }
14844             enc = "o:" + flat.substring(0, flat.length-1);
14845         }else{
14846             enc = "s:" + v;
14847         }
14848         return escape(enc);        
14849     }
14850 });
14851
14852 /*
14853  * Based on:
14854  * Ext JS Library 1.1.1
14855  * Copyright(c) 2006-2007, Ext JS, LLC.
14856  *
14857  * Originally Released Under LGPL - original licence link has changed is not relivant.
14858  *
14859  * Fork - LGPL
14860  * <script type="text/javascript">
14861  */
14862 /**
14863  * @class Roo.state.Manager
14864  * This is the global state manager. By default all components that are "state aware" check this class
14865  * for state information if you don't pass them a custom state provider. In order for this class
14866  * to be useful, it must be initialized with a provider when your application initializes.
14867  <pre><code>
14868 // in your initialization function
14869 init : function(){
14870    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14871    ...
14872    // supposed you have a {@link Roo.BorderLayout}
14873    var layout = new Roo.BorderLayout(...);
14874    layout.restoreState();
14875    // or a {Roo.BasicDialog}
14876    var dialog = new Roo.BasicDialog(...);
14877    dialog.restoreState();
14878  </code></pre>
14879  * @singleton
14880  */
14881 Roo.state.Manager = function(){
14882     var provider = new Roo.state.Provider();
14883     
14884     return {
14885         /**
14886          * Configures the default state provider for your application
14887          * @param {Provider} stateProvider The state provider to set
14888          */
14889         setProvider : function(stateProvider){
14890             provider = stateProvider;
14891         },
14892         
14893         /**
14894          * Returns the current value for a key
14895          * @param {String} name The key name
14896          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14897          * @return {Mixed} The state data
14898          */
14899         get : function(key, defaultValue){
14900             return provider.get(key, defaultValue);
14901         },
14902         
14903         /**
14904          * Sets the value for a key
14905          * @param {String} name The key name
14906          * @param {Mixed} value The state data
14907          */
14908          set : function(key, value){
14909             provider.set(key, value);
14910         },
14911         
14912         /**
14913          * Clears a value from the state
14914          * @param {String} name The key name
14915          */
14916         clear : function(key){
14917             provider.clear(key);
14918         },
14919         
14920         /**
14921          * Gets the currently configured state provider
14922          * @return {Provider} The state provider
14923          */
14924         getProvider : function(){
14925             return provider;
14926         }
14927     };
14928 }();
14929 /*
14930  * Based on:
14931  * Ext JS Library 1.1.1
14932  * Copyright(c) 2006-2007, Ext JS, LLC.
14933  *
14934  * Originally Released Under LGPL - original licence link has changed is not relivant.
14935  *
14936  * Fork - LGPL
14937  * <script type="text/javascript">
14938  */
14939 /**
14940  * @class Roo.state.CookieProvider
14941  * @extends Roo.state.Provider
14942  * The default Provider implementation which saves state via cookies.
14943  * <br />Usage:
14944  <pre><code>
14945    var cp = new Roo.state.CookieProvider({
14946        path: "/cgi-bin/",
14947        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14948        domain: "roojs.com"
14949    })
14950    Roo.state.Manager.setProvider(cp);
14951  </code></pre>
14952  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14953  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14954  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14955  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14956  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14957  * domain the page is running on including the 'www' like 'www.roojs.com')
14958  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14959  * @constructor
14960  * Create a new CookieProvider
14961  * @param {Object} config The configuration object
14962  */
14963 Roo.state.CookieProvider = function(config){
14964     Roo.state.CookieProvider.superclass.constructor.call(this);
14965     this.path = "/";
14966     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14967     this.domain = null;
14968     this.secure = false;
14969     Roo.apply(this, config);
14970     this.state = this.readCookies();
14971 };
14972
14973 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14974     // private
14975     set : function(name, value){
14976         if(typeof value == "undefined" || value === null){
14977             this.clear(name);
14978             return;
14979         }
14980         this.setCookie(name, value);
14981         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14982     },
14983
14984     // private
14985     clear : function(name){
14986         this.clearCookie(name);
14987         Roo.state.CookieProvider.superclass.clear.call(this, name);
14988     },
14989
14990     // private
14991     readCookies : function(){
14992         var cookies = {};
14993         var c = document.cookie + ";";
14994         var re = /\s?(.*?)=(.*?);/g;
14995         var matches;
14996         while((matches = re.exec(c)) != null){
14997             var name = matches[1];
14998             var value = matches[2];
14999             if(name && name.substring(0,3) == "ys-"){
15000                 cookies[name.substr(3)] = this.decodeValue(value);
15001             }
15002         }
15003         return cookies;
15004     },
15005
15006     // private
15007     setCookie : function(name, value){
15008         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15009            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15010            ((this.path == null) ? "" : ("; path=" + this.path)) +
15011            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15012            ((this.secure == true) ? "; secure" : "");
15013     },
15014
15015     // private
15016     clearCookie : function(name){
15017         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15018            ((this.path == null) ? "" : ("; path=" + this.path)) +
15019            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15020            ((this.secure == true) ? "; secure" : "");
15021     }
15022 });/*
15023  * Based on:
15024  * Ext JS Library 1.1.1
15025  * Copyright(c) 2006-2007, Ext JS, LLC.
15026  *
15027  * Originally Released Under LGPL - original licence link has changed is not relivant.
15028  *
15029  * Fork - LGPL
15030  * <script type="text/javascript">
15031  */
15032  
15033
15034 /**
15035  * @class Roo.ComponentMgr
15036  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15037  * @singleton
15038  */
15039 Roo.ComponentMgr = function(){
15040     var all = new Roo.util.MixedCollection();
15041
15042     return {
15043         /**
15044          * Registers a component.
15045          * @param {Roo.Component} c The component
15046          */
15047         register : function(c){
15048             all.add(c);
15049         },
15050
15051         /**
15052          * Unregisters a component.
15053          * @param {Roo.Component} c The component
15054          */
15055         unregister : function(c){
15056             all.remove(c);
15057         },
15058
15059         /**
15060          * Returns a component by id
15061          * @param {String} id The component id
15062          */
15063         get : function(id){
15064             return all.get(id);
15065         },
15066
15067         /**
15068          * Registers a function that will be called when a specified component is added to ComponentMgr
15069          * @param {String} id The component id
15070          * @param {Funtction} fn The callback function
15071          * @param {Object} scope The scope of the callback
15072          */
15073         onAvailable : function(id, fn, scope){
15074             all.on("add", function(index, o){
15075                 if(o.id == id){
15076                     fn.call(scope || o, o);
15077                     all.un("add", fn, scope);
15078                 }
15079             });
15080         }
15081     };
15082 }();/*
15083  * Based on:
15084  * Ext JS Library 1.1.1
15085  * Copyright(c) 2006-2007, Ext JS, LLC.
15086  *
15087  * Originally Released Under LGPL - original licence link has changed is not relivant.
15088  *
15089  * Fork - LGPL
15090  * <script type="text/javascript">
15091  */
15092  
15093 /**
15094  * @class Roo.Component
15095  * @extends Roo.util.Observable
15096  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15097  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15098  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15099  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15100  * All visual components (widgets) that require rendering into a layout should subclass Component.
15101  * @constructor
15102  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15103  * 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
15104  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15105  */
15106 Roo.Component = function(config){
15107     config = config || {};
15108     if(config.tagName || config.dom || typeof config == "string"){ // element object
15109         config = {el: config, id: config.id || config};
15110     }
15111     this.initialConfig = config;
15112
15113     Roo.apply(this, config);
15114     this.addEvents({
15115         /**
15116          * @event disable
15117          * Fires after the component is disabled.
15118              * @param {Roo.Component} this
15119              */
15120         disable : true,
15121         /**
15122          * @event enable
15123          * Fires after the component is enabled.
15124              * @param {Roo.Component} this
15125              */
15126         enable : true,
15127         /**
15128          * @event beforeshow
15129          * Fires before the component is shown.  Return false to stop the show.
15130              * @param {Roo.Component} this
15131              */
15132         beforeshow : true,
15133         /**
15134          * @event show
15135          * Fires after the component is shown.
15136              * @param {Roo.Component} this
15137              */
15138         show : true,
15139         /**
15140          * @event beforehide
15141          * Fires before the component is hidden. Return false to stop the hide.
15142              * @param {Roo.Component} this
15143              */
15144         beforehide : true,
15145         /**
15146          * @event hide
15147          * Fires after the component is hidden.
15148              * @param {Roo.Component} this
15149              */
15150         hide : true,
15151         /**
15152          * @event beforerender
15153          * Fires before the component is rendered. Return false to stop the render.
15154              * @param {Roo.Component} this
15155              */
15156         beforerender : true,
15157         /**
15158          * @event render
15159          * Fires after the component is rendered.
15160              * @param {Roo.Component} this
15161              */
15162         render : true,
15163         /**
15164          * @event beforedestroy
15165          * Fires before the component is destroyed. Return false to stop the destroy.
15166              * @param {Roo.Component} this
15167              */
15168         beforedestroy : true,
15169         /**
15170          * @event destroy
15171          * Fires after the component is destroyed.
15172              * @param {Roo.Component} this
15173              */
15174         destroy : true
15175     });
15176     if(!this.id){
15177         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15178     }
15179     Roo.ComponentMgr.register(this);
15180     Roo.Component.superclass.constructor.call(this);
15181     this.initComponent();
15182     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15183         this.render(this.renderTo);
15184         delete this.renderTo;
15185     }
15186 };
15187
15188 /** @private */
15189 Roo.Component.AUTO_ID = 1000;
15190
15191 Roo.extend(Roo.Component, Roo.util.Observable, {
15192     /**
15193      * @scope Roo.Component.prototype
15194      * @type {Boolean}
15195      * true if this component is hidden. Read-only.
15196      */
15197     hidden : false,
15198     /**
15199      * @type {Boolean}
15200      * true if this component is disabled. Read-only.
15201      */
15202     disabled : false,
15203     /**
15204      * @type {Boolean}
15205      * true if this component has been rendered. Read-only.
15206      */
15207     rendered : false,
15208     
15209     /** @cfg {String} disableClass
15210      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15211      */
15212     disabledClass : "x-item-disabled",
15213         /** @cfg {Boolean} allowDomMove
15214          * Whether the component can move the Dom node when rendering (defaults to true).
15215          */
15216     allowDomMove : true,
15217     /** @cfg {String} hideMode (display|visibility)
15218      * How this component should hidden. Supported values are
15219      * "visibility" (css visibility), "offsets" (negative offset position) and
15220      * "display" (css display) - defaults to "display".
15221      */
15222     hideMode: 'display',
15223
15224     /** @private */
15225     ctype : "Roo.Component",
15226
15227     /**
15228      * @cfg {String} actionMode 
15229      * which property holds the element that used for  hide() / show() / disable() / enable()
15230      * default is 'el' 
15231      */
15232     actionMode : "el",
15233
15234     /** @private */
15235     getActionEl : function(){
15236         return this[this.actionMode];
15237     },
15238
15239     initComponent : Roo.emptyFn,
15240     /**
15241      * If this is a lazy rendering component, render it to its container element.
15242      * @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.
15243      */
15244     render : function(container, position){
15245         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15246             if(!container && this.el){
15247                 this.el = Roo.get(this.el);
15248                 container = this.el.dom.parentNode;
15249                 this.allowDomMove = false;
15250             }
15251             this.container = Roo.get(container);
15252             this.rendered = true;
15253             if(position !== undefined){
15254                 if(typeof position == 'number'){
15255                     position = this.container.dom.childNodes[position];
15256                 }else{
15257                     position = Roo.getDom(position);
15258                 }
15259             }
15260             this.onRender(this.container, position || null);
15261             if(this.cls){
15262                 this.el.addClass(this.cls);
15263                 delete this.cls;
15264             }
15265             if(this.style){
15266                 this.el.applyStyles(this.style);
15267                 delete this.style;
15268             }
15269             this.fireEvent("render", this);
15270             this.afterRender(this.container);
15271             if(this.hidden){
15272                 this.hide();
15273             }
15274             if(this.disabled){
15275                 this.disable();
15276             }
15277         }
15278         return this;
15279     },
15280
15281     /** @private */
15282     // default function is not really useful
15283     onRender : function(ct, position){
15284         if(this.el){
15285             this.el = Roo.get(this.el);
15286             if(this.allowDomMove !== false){
15287                 ct.dom.insertBefore(this.el.dom, position);
15288             }
15289         }
15290     },
15291
15292     /** @private */
15293     getAutoCreate : function(){
15294         var cfg = typeof this.autoCreate == "object" ?
15295                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15296         if(this.id && !cfg.id){
15297             cfg.id = this.id;
15298         }
15299         return cfg;
15300     },
15301
15302     /** @private */
15303     afterRender : Roo.emptyFn,
15304
15305     /**
15306      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15307      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15308      */
15309     destroy : function(){
15310         if(this.fireEvent("beforedestroy", this) !== false){
15311             this.purgeListeners();
15312             this.beforeDestroy();
15313             if(this.rendered){
15314                 this.el.removeAllListeners();
15315                 this.el.remove();
15316                 if(this.actionMode == "container"){
15317                     this.container.remove();
15318                 }
15319             }
15320             this.onDestroy();
15321             Roo.ComponentMgr.unregister(this);
15322             this.fireEvent("destroy", this);
15323         }
15324     },
15325
15326         /** @private */
15327     beforeDestroy : function(){
15328
15329     },
15330
15331         /** @private */
15332         onDestroy : function(){
15333
15334     },
15335
15336     /**
15337      * Returns the underlying {@link Roo.Element}.
15338      * @return {Roo.Element} The element
15339      */
15340     getEl : function(){
15341         return this.el;
15342     },
15343
15344     /**
15345      * Returns the id of this component.
15346      * @return {String}
15347      */
15348     getId : function(){
15349         return this.id;
15350     },
15351
15352     /**
15353      * Try to focus this component.
15354      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15355      * @return {Roo.Component} this
15356      */
15357     focus : function(selectText){
15358         if(this.rendered){
15359             this.el.focus();
15360             if(selectText === true){
15361                 this.el.dom.select();
15362             }
15363         }
15364         return this;
15365     },
15366
15367     /** @private */
15368     blur : function(){
15369         if(this.rendered){
15370             this.el.blur();
15371         }
15372         return this;
15373     },
15374
15375     /**
15376      * Disable this component.
15377      * @return {Roo.Component} this
15378      */
15379     disable : function(){
15380         if(this.rendered){
15381             this.onDisable();
15382         }
15383         this.disabled = true;
15384         this.fireEvent("disable", this);
15385         return this;
15386     },
15387
15388         // private
15389     onDisable : function(){
15390         this.getActionEl().addClass(this.disabledClass);
15391         this.el.dom.disabled = true;
15392     },
15393
15394     /**
15395      * Enable this component.
15396      * @return {Roo.Component} this
15397      */
15398     enable : function(){
15399         if(this.rendered){
15400             this.onEnable();
15401         }
15402         this.disabled = false;
15403         this.fireEvent("enable", this);
15404         return this;
15405     },
15406
15407         // private
15408     onEnable : function(){
15409         this.getActionEl().removeClass(this.disabledClass);
15410         this.el.dom.disabled = false;
15411     },
15412
15413     /**
15414      * Convenience function for setting disabled/enabled by boolean.
15415      * @param {Boolean} disabled
15416      */
15417     setDisabled : function(disabled){
15418         this[disabled ? "disable" : "enable"]();
15419     },
15420
15421     /**
15422      * Show this component.
15423      * @return {Roo.Component} this
15424      */
15425     show: function(){
15426         if(this.fireEvent("beforeshow", this) !== false){
15427             this.hidden = false;
15428             if(this.rendered){
15429                 this.onShow();
15430             }
15431             this.fireEvent("show", this);
15432         }
15433         return this;
15434     },
15435
15436     // private
15437     onShow : function(){
15438         var ae = this.getActionEl();
15439         if(this.hideMode == 'visibility'){
15440             ae.dom.style.visibility = "visible";
15441         }else if(this.hideMode == 'offsets'){
15442             ae.removeClass('x-hidden');
15443         }else{
15444             ae.dom.style.display = "";
15445         }
15446     },
15447
15448     /**
15449      * Hide this component.
15450      * @return {Roo.Component} this
15451      */
15452     hide: function(){
15453         if(this.fireEvent("beforehide", this) !== false){
15454             this.hidden = true;
15455             if(this.rendered){
15456                 this.onHide();
15457             }
15458             this.fireEvent("hide", this);
15459         }
15460         return this;
15461     },
15462
15463     // private
15464     onHide : function(){
15465         var ae = this.getActionEl();
15466         if(this.hideMode == 'visibility'){
15467             ae.dom.style.visibility = "hidden";
15468         }else if(this.hideMode == 'offsets'){
15469             ae.addClass('x-hidden');
15470         }else{
15471             ae.dom.style.display = "none";
15472         }
15473     },
15474
15475     /**
15476      * Convenience function to hide or show this component by boolean.
15477      * @param {Boolean} visible True to show, false to hide
15478      * @return {Roo.Component} this
15479      */
15480     setVisible: function(visible){
15481         if(visible) {
15482             this.show();
15483         }else{
15484             this.hide();
15485         }
15486         return this;
15487     },
15488
15489     /**
15490      * Returns true if this component is visible.
15491      */
15492     isVisible : function(){
15493         return this.getActionEl().isVisible();
15494     },
15495
15496     cloneConfig : function(overrides){
15497         overrides = overrides || {};
15498         var id = overrides.id || Roo.id();
15499         var cfg = Roo.applyIf(overrides, this.initialConfig);
15500         cfg.id = id; // prevent dup id
15501         return new this.constructor(cfg);
15502     }
15503 });/*
15504  * Based on:
15505  * Ext JS Library 1.1.1
15506  * Copyright(c) 2006-2007, Ext JS, LLC.
15507  *
15508  * Originally Released Under LGPL - original licence link has changed is not relivant.
15509  *
15510  * Fork - LGPL
15511  * <script type="text/javascript">
15512  */
15513
15514 /**
15515  * @class Roo.BoxComponent
15516  * @extends Roo.Component
15517  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15518  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15519  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15520  * layout containers.
15521  * @constructor
15522  * @param {Roo.Element/String/Object} config The configuration options.
15523  */
15524 Roo.BoxComponent = function(config){
15525     Roo.Component.call(this, config);
15526     this.addEvents({
15527         /**
15528          * @event resize
15529          * Fires after the component is resized.
15530              * @param {Roo.Component} this
15531              * @param {Number} adjWidth The box-adjusted width that was set
15532              * @param {Number} adjHeight The box-adjusted height that was set
15533              * @param {Number} rawWidth The width that was originally specified
15534              * @param {Number} rawHeight The height that was originally specified
15535              */
15536         resize : true,
15537         /**
15538          * @event move
15539          * Fires after the component is moved.
15540              * @param {Roo.Component} this
15541              * @param {Number} x The new x position
15542              * @param {Number} y The new y position
15543              */
15544         move : true
15545     });
15546 };
15547
15548 Roo.extend(Roo.BoxComponent, Roo.Component, {
15549     // private, set in afterRender to signify that the component has been rendered
15550     boxReady : false,
15551     // private, used to defer height settings to subclasses
15552     deferHeight: false,
15553     /** @cfg {Number} width
15554      * width (optional) size of component
15555      */
15556      /** @cfg {Number} height
15557      * height (optional) size of component
15558      */
15559      
15560     /**
15561      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15562      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15563      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15564      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15565      * @return {Roo.BoxComponent} this
15566      */
15567     setSize : function(w, h){
15568         // support for standard size objects
15569         if(typeof w == 'object'){
15570             h = w.height;
15571             w = w.width;
15572         }
15573         // not rendered
15574         if(!this.boxReady){
15575             this.width = w;
15576             this.height = h;
15577             return this;
15578         }
15579
15580         // prevent recalcs when not needed
15581         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15582             return this;
15583         }
15584         this.lastSize = {width: w, height: h};
15585
15586         var adj = this.adjustSize(w, h);
15587         var aw = adj.width, ah = adj.height;
15588         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15589             var rz = this.getResizeEl();
15590             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15591                 rz.setSize(aw, ah);
15592             }else if(!this.deferHeight && ah !== undefined){
15593                 rz.setHeight(ah);
15594             }else if(aw !== undefined){
15595                 rz.setWidth(aw);
15596             }
15597             this.onResize(aw, ah, w, h);
15598             this.fireEvent('resize', this, aw, ah, w, h);
15599         }
15600         return this;
15601     },
15602
15603     /**
15604      * Gets the current size of the component's underlying element.
15605      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15606      */
15607     getSize : function(){
15608         return this.el.getSize();
15609     },
15610
15611     /**
15612      * Gets the current XY position of the component's underlying element.
15613      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15614      * @return {Array} The XY position of the element (e.g., [100, 200])
15615      */
15616     getPosition : function(local){
15617         if(local === true){
15618             return [this.el.getLeft(true), this.el.getTop(true)];
15619         }
15620         return this.xy || this.el.getXY();
15621     },
15622
15623     /**
15624      * Gets the current box measurements of the component's underlying element.
15625      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15626      * @returns {Object} box An object in the format {x, y, width, height}
15627      */
15628     getBox : function(local){
15629         var s = this.el.getSize();
15630         if(local){
15631             s.x = this.el.getLeft(true);
15632             s.y = this.el.getTop(true);
15633         }else{
15634             var xy = this.xy || this.el.getXY();
15635             s.x = xy[0];
15636             s.y = xy[1];
15637         }
15638         return s;
15639     },
15640
15641     /**
15642      * Sets the current box measurements of the component's underlying element.
15643      * @param {Object} box An object in the format {x, y, width, height}
15644      * @returns {Roo.BoxComponent} this
15645      */
15646     updateBox : function(box){
15647         this.setSize(box.width, box.height);
15648         this.setPagePosition(box.x, box.y);
15649         return this;
15650     },
15651
15652     // protected
15653     getResizeEl : function(){
15654         return this.resizeEl || this.el;
15655     },
15656
15657     // protected
15658     getPositionEl : function(){
15659         return this.positionEl || this.el;
15660     },
15661
15662     /**
15663      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15664      * This method fires the move event.
15665      * @param {Number} left The new left
15666      * @param {Number} top The new top
15667      * @returns {Roo.BoxComponent} this
15668      */
15669     setPosition : function(x, y){
15670         this.x = x;
15671         this.y = y;
15672         if(!this.boxReady){
15673             return this;
15674         }
15675         var adj = this.adjustPosition(x, y);
15676         var ax = adj.x, ay = adj.y;
15677
15678         var el = this.getPositionEl();
15679         if(ax !== undefined || ay !== undefined){
15680             if(ax !== undefined && ay !== undefined){
15681                 el.setLeftTop(ax, ay);
15682             }else if(ax !== undefined){
15683                 el.setLeft(ax);
15684             }else if(ay !== undefined){
15685                 el.setTop(ay);
15686             }
15687             this.onPosition(ax, ay);
15688             this.fireEvent('move', this, ax, ay);
15689         }
15690         return this;
15691     },
15692
15693     /**
15694      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15695      * This method fires the move event.
15696      * @param {Number} x The new x position
15697      * @param {Number} y The new y position
15698      * @returns {Roo.BoxComponent} this
15699      */
15700     setPagePosition : function(x, y){
15701         this.pageX = x;
15702         this.pageY = y;
15703         if(!this.boxReady){
15704             return;
15705         }
15706         if(x === undefined || y === undefined){ // cannot translate undefined points
15707             return;
15708         }
15709         var p = this.el.translatePoints(x, y);
15710         this.setPosition(p.left, p.top);
15711         return this;
15712     },
15713
15714     // private
15715     onRender : function(ct, position){
15716         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15717         if(this.resizeEl){
15718             this.resizeEl = Roo.get(this.resizeEl);
15719         }
15720         if(this.positionEl){
15721             this.positionEl = Roo.get(this.positionEl);
15722         }
15723     },
15724
15725     // private
15726     afterRender : function(){
15727         Roo.BoxComponent.superclass.afterRender.call(this);
15728         this.boxReady = true;
15729         this.setSize(this.width, this.height);
15730         if(this.x || this.y){
15731             this.setPosition(this.x, this.y);
15732         }
15733         if(this.pageX || this.pageY){
15734             this.setPagePosition(this.pageX, this.pageY);
15735         }
15736     },
15737
15738     /**
15739      * Force the component's size to recalculate based on the underlying element's current height and width.
15740      * @returns {Roo.BoxComponent} this
15741      */
15742     syncSize : function(){
15743         delete this.lastSize;
15744         this.setSize(this.el.getWidth(), this.el.getHeight());
15745         return this;
15746     },
15747
15748     /**
15749      * Called after the component is resized, this method is empty by default but can be implemented by any
15750      * subclass that needs to perform custom logic after a resize occurs.
15751      * @param {Number} adjWidth The box-adjusted width that was set
15752      * @param {Number} adjHeight The box-adjusted height that was set
15753      * @param {Number} rawWidth The width that was originally specified
15754      * @param {Number} rawHeight The height that was originally specified
15755      */
15756     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15757
15758     },
15759
15760     /**
15761      * Called after the component is moved, this method is empty by default but can be implemented by any
15762      * subclass that needs to perform custom logic after a move occurs.
15763      * @param {Number} x The new x position
15764      * @param {Number} y The new y position
15765      */
15766     onPosition : function(x, y){
15767
15768     },
15769
15770     // private
15771     adjustSize : function(w, h){
15772         if(this.autoWidth){
15773             w = 'auto';
15774         }
15775         if(this.autoHeight){
15776             h = 'auto';
15777         }
15778         return {width : w, height: h};
15779     },
15780
15781     // private
15782     adjustPosition : function(x, y){
15783         return {x : x, y: y};
15784     }
15785 });/*
15786  * Original code for Roojs - LGPL
15787  * <script type="text/javascript">
15788  */
15789  
15790 /**
15791  * @class Roo.XComponent
15792  * A delayed Element creator...
15793  * Or a way to group chunks of interface together.
15794  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15795  *  used in conjunction with XComponent.build() it will create an instance of each element,
15796  *  then call addxtype() to build the User interface.
15797  * 
15798  * Mypart.xyx = new Roo.XComponent({
15799
15800     parent : 'Mypart.xyz', // empty == document.element.!!
15801     order : '001',
15802     name : 'xxxx'
15803     region : 'xxxx'
15804     disabled : function() {} 
15805      
15806     tree : function() { // return an tree of xtype declared components
15807         var MODULE = this;
15808         return 
15809         {
15810             xtype : 'NestedLayoutPanel',
15811             // technicall
15812         }
15813      ]
15814  *})
15815  *
15816  *
15817  * It can be used to build a big heiracy, with parent etc.
15818  * or you can just use this to render a single compoent to a dom element
15819  * MYPART.render(Roo.Element | String(id) | dom_element )
15820  *
15821  *
15822  * Usage patterns.
15823  *
15824  * Classic Roo
15825  *
15826  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15827  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15828  *
15829  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15830  *
15831  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15832  * - if mulitple topModules exist, the last one is defined as the top module.
15833  *
15834  * Embeded Roo
15835  * 
15836  * When the top level or multiple modules are to embedded into a existing HTML page,
15837  * the parent element can container '#id' of the element where the module will be drawn.
15838  *
15839  * Bootstrap Roo
15840  *
15841  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15842  * it relies more on a include mechanism, where sub modules are included into an outer page.
15843  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15844  * 
15845  * Bootstrap Roo Included elements
15846  *
15847  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15848  * hence confusing the component builder as it thinks there are multiple top level elements. 
15849  *
15850  * 
15851  * 
15852  * @extends Roo.util.Observable
15853  * @constructor
15854  * @param cfg {Object} configuration of component
15855  * 
15856  */
15857 Roo.XComponent = function(cfg) {
15858     Roo.apply(this, cfg);
15859     this.addEvents({ 
15860         /**
15861              * @event built
15862              * Fires when this the componnt is built
15863              * @param {Roo.XComponent} c the component
15864              */
15865         'built' : true
15866         
15867     });
15868     this.region = this.region || 'center'; // default..
15869     Roo.XComponent.register(this);
15870     this.modules = false;
15871     this.el = false; // where the layout goes..
15872     
15873     
15874 }
15875 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15876     /**
15877      * @property el
15878      * The created element (with Roo.factory())
15879      * @type {Roo.Layout}
15880      */
15881     el  : false,
15882     
15883     /**
15884      * @property el
15885      * for BC  - use el in new code
15886      * @type {Roo.Layout}
15887      */
15888     panel : false,
15889     
15890     /**
15891      * @property layout
15892      * for BC  - use el in new code
15893      * @type {Roo.Layout}
15894      */
15895     layout : false,
15896     
15897      /**
15898      * @cfg {Function|boolean} disabled
15899      * If this module is disabled by some rule, return true from the funtion
15900      */
15901     disabled : false,
15902     
15903     /**
15904      * @cfg {String} parent 
15905      * Name of parent element which it get xtype added to..
15906      */
15907     parent: false,
15908     
15909     /**
15910      * @cfg {String} order
15911      * Used to set the order in which elements are created (usefull for multiple tabs)
15912      */
15913     
15914     order : false,
15915     /**
15916      * @cfg {String} name
15917      * String to display while loading.
15918      */
15919     name : false,
15920     /**
15921      * @cfg {String} region
15922      * Region to render component to (defaults to center)
15923      */
15924     region : 'center',
15925     
15926     /**
15927      * @cfg {Array} items
15928      * A single item array - the first element is the root of the tree..
15929      * It's done this way to stay compatible with the Xtype system...
15930      */
15931     items : false,
15932     
15933     /**
15934      * @property _tree
15935      * The method that retuns the tree of parts that make up this compoennt 
15936      * @type {function}
15937      */
15938     _tree  : false,
15939     
15940      /**
15941      * render
15942      * render element to dom or tree
15943      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15944      */
15945     
15946     render : function(el)
15947     {
15948         
15949         el = el || false;
15950         var hp = this.parent ? 1 : 0;
15951         Roo.debug &&  Roo.log(this);
15952         
15953         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15954             // if parent is a '#.....' string, then let's use that..
15955             var ename = this.parent.substr(1);
15956             this.parent = false;
15957             Roo.debug && Roo.log(ename);
15958             switch (ename) {
15959                 case 'bootstrap-body' :
15960                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15961                         this.parent = { el :  new  Roo.bootstrap.Body() };
15962                         Roo.debug && Roo.log("setting el to doc body");
15963                          
15964                     } else {
15965                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15966                     }
15967                     break;
15968                 case 'bootstrap':
15969                     this.parent = { el : true};
15970                     // fall through
15971                 default:
15972                     el = Roo.get(ename);
15973                     break;
15974             }
15975                 
15976             
15977             if (!el && !this.parent) {
15978                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15979                 return;
15980             }
15981         }
15982         Roo.debug && Roo.log("EL:");
15983         Roo.debug && Roo.log(el);
15984         Roo.debug && Roo.log("this.parent.el:");
15985         Roo.debug && Roo.log(this.parent.el);
15986         
15987         var tree = this._tree ? this._tree() : this.tree();
15988
15989         // altertive root elements ??? - we need a better way to indicate these.
15990         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15991                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15992         
15993         if (!this.parent && is_alt) {
15994             //el = Roo.get(document.body);
15995             this.parent = { el : true };
15996         }
15997             
15998             
15999         
16000         if (!this.parent) {
16001             
16002             Roo.debug && Roo.log("no parent - creating one");
16003             
16004             el = el ? Roo.get(el) : false;      
16005             
16006             // it's a top level one..
16007             this.parent =  {
16008                 el : new Roo.BorderLayout(el || document.body, {
16009                 
16010                      center: {
16011                          titlebar: false,
16012                          autoScroll:false,
16013                          closeOnTab: true,
16014                          tabPosition: 'top',
16015                           //resizeTabs: true,
16016                          alwaysShowTabs: el && hp? false :  true,
16017                          hideTabs: el || !hp ? true :  false,
16018                          minTabWidth: 140
16019                      }
16020                  })
16021             }
16022         }
16023         
16024         if (!this.parent.el) {
16025                 // probably an old style ctor, which has been disabled.
16026                 return;
16027
16028         }
16029                 // The 'tree' method is  '_tree now' 
16030             
16031         tree.region = tree.region || this.region;
16032         
16033         if (this.parent.el === true) {
16034             // bootstrap... - body..
16035             this.parent.el = Roo.factory(tree);
16036         }
16037         
16038         this.el = this.parent.el.addxtype(tree);
16039         this.fireEvent('built', this);
16040         
16041         this.panel = this.el;
16042         this.layout = this.panel.layout;
16043         this.parentLayout = this.parent.layout  || false;  
16044          
16045     }
16046     
16047 });
16048
16049 Roo.apply(Roo.XComponent, {
16050     /**
16051      * @property  hideProgress
16052      * true to disable the building progress bar.. usefull on single page renders.
16053      * @type Boolean
16054      */
16055     hideProgress : false,
16056     /**
16057      * @property  buildCompleted
16058      * True when the builder has completed building the interface.
16059      * @type Boolean
16060      */
16061     buildCompleted : false,
16062      
16063     /**
16064      * @property  topModule
16065      * the upper most module - uses document.element as it's constructor.
16066      * @type Object
16067      */
16068      
16069     topModule  : false,
16070       
16071     /**
16072      * @property  modules
16073      * array of modules to be created by registration system.
16074      * @type {Array} of Roo.XComponent
16075      */
16076     
16077     modules : [],
16078     /**
16079      * @property  elmodules
16080      * array of modules to be created by which use #ID 
16081      * @type {Array} of Roo.XComponent
16082      */
16083      
16084     elmodules : [],
16085
16086      /**
16087      * @property  build_from_html
16088      * Build elements from html - used by bootstrap HTML stuff 
16089      *    - this is cleared after build is completed
16090      * @type {boolean} true  (default false)
16091      */
16092      
16093     build_from_html : false,
16094
16095     /**
16096      * Register components to be built later.
16097      *
16098      * This solves the following issues
16099      * - Building is not done on page load, but after an authentication process has occured.
16100      * - Interface elements are registered on page load
16101      * - Parent Interface elements may not be loaded before child, so this handles that..
16102      * 
16103      *
16104      * example:
16105      * 
16106      * MyApp.register({
16107           order : '000001',
16108           module : 'Pman.Tab.projectMgr',
16109           region : 'center',
16110           parent : 'Pman.layout',
16111           disabled : false,  // or use a function..
16112         })
16113      
16114      * * @param {Object} details about module
16115      */
16116     register : function(obj) {
16117                 
16118         Roo.XComponent.event.fireEvent('register', obj);
16119         switch(typeof(obj.disabled) ) {
16120                 
16121             case 'undefined':
16122                 break;
16123             
16124             case 'function':
16125                 if ( obj.disabled() ) {
16126                         return;
16127                 }
16128                 break;
16129             
16130             default:
16131                 if (obj.disabled) {
16132                         return;
16133                 }
16134                 break;
16135         }
16136                 
16137         this.modules.push(obj);
16138          
16139     },
16140     /**
16141      * convert a string to an object..
16142      * eg. 'AAA.BBB' -> finds AAA.BBB
16143
16144      */
16145     
16146     toObject : function(str)
16147     {
16148         if (!str || typeof(str) == 'object') {
16149             return str;
16150         }
16151         if (str.substring(0,1) == '#') {
16152             return str;
16153         }
16154
16155         var ar = str.split('.');
16156         var rt, o;
16157         rt = ar.shift();
16158             /** eval:var:o */
16159         try {
16160             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16161         } catch (e) {
16162             throw "Module not found : " + str;
16163         }
16164         
16165         if (o === false) {
16166             throw "Module not found : " + str;
16167         }
16168         Roo.each(ar, function(e) {
16169             if (typeof(o[e]) == 'undefined') {
16170                 throw "Module not found : " + str;
16171             }
16172             o = o[e];
16173         });
16174         
16175         return o;
16176         
16177     },
16178     
16179     
16180     /**
16181      * move modules into their correct place in the tree..
16182      * 
16183      */
16184     preBuild : function ()
16185     {
16186         var _t = this;
16187         Roo.each(this.modules , function (obj)
16188         {
16189             Roo.XComponent.event.fireEvent('beforebuild', obj);
16190             
16191             var opar = obj.parent;
16192             try { 
16193                 obj.parent = this.toObject(opar);
16194             } catch(e) {
16195                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16196                 return;
16197             }
16198             
16199             if (!obj.parent) {
16200                 Roo.debug && Roo.log("GOT top level module");
16201                 Roo.debug && Roo.log(obj);
16202                 obj.modules = new Roo.util.MixedCollection(false, 
16203                     function(o) { return o.order + '' }
16204                 );
16205                 this.topModule = obj;
16206                 return;
16207             }
16208                         // parent is a string (usually a dom element name..)
16209             if (typeof(obj.parent) == 'string') {
16210                 this.elmodules.push(obj);
16211                 return;
16212             }
16213             if (obj.parent.constructor != Roo.XComponent) {
16214                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16215             }
16216             if (!obj.parent.modules) {
16217                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16218                     function(o) { return o.order + '' }
16219                 );
16220             }
16221             if (obj.parent.disabled) {
16222                 obj.disabled = true;
16223             }
16224             obj.parent.modules.add(obj);
16225         }, this);
16226     },
16227     
16228      /**
16229      * make a list of modules to build.
16230      * @return {Array} list of modules. 
16231      */ 
16232     
16233     buildOrder : function()
16234     {
16235         var _this = this;
16236         var cmp = function(a,b) {   
16237             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16238         };
16239         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16240             throw "No top level modules to build";
16241         }
16242         
16243         // make a flat list in order of modules to build.
16244         var mods = this.topModule ? [ this.topModule ] : [];
16245                 
16246         
16247         // elmodules (is a list of DOM based modules )
16248         Roo.each(this.elmodules, function(e) {
16249             mods.push(e);
16250             if (!this.topModule &&
16251                 typeof(e.parent) == 'string' &&
16252                 e.parent.substring(0,1) == '#' &&
16253                 Roo.get(e.parent.substr(1))
16254                ) {
16255                 
16256                 _this.topModule = e;
16257             }
16258             
16259         });
16260
16261         
16262         // add modules to their parents..
16263         var addMod = function(m) {
16264             Roo.debug && Roo.log("build Order: add: " + m.name);
16265                 
16266             mods.push(m);
16267             if (m.modules && !m.disabled) {
16268                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16269                 m.modules.keySort('ASC',  cmp );
16270                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16271     
16272                 m.modules.each(addMod);
16273             } else {
16274                 Roo.debug && Roo.log("build Order: no child modules");
16275             }
16276             // not sure if this is used any more..
16277             if (m.finalize) {
16278                 m.finalize.name = m.name + " (clean up) ";
16279                 mods.push(m.finalize);
16280             }
16281             
16282         }
16283         if (this.topModule && this.topModule.modules) { 
16284             this.topModule.modules.keySort('ASC',  cmp );
16285             this.topModule.modules.each(addMod);
16286         } 
16287         return mods;
16288     },
16289     
16290      /**
16291      * Build the registered modules.
16292      * @param {Object} parent element.
16293      * @param {Function} optional method to call after module has been added.
16294      * 
16295      */ 
16296    
16297     build : function(opts) 
16298     {
16299         
16300         if (typeof(opts) != 'undefined') {
16301             Roo.apply(this,opts);
16302         }
16303         
16304         this.preBuild();
16305         var mods = this.buildOrder();
16306       
16307         //this.allmods = mods;
16308         //Roo.debug && Roo.log(mods);
16309         //return;
16310         if (!mods.length) { // should not happen
16311             throw "NO modules!!!";
16312         }
16313         
16314         
16315         var msg = "Building Interface...";
16316         // flash it up as modal - so we store the mask!?
16317         if (!this.hideProgress && Roo.MessageBox) {
16318             Roo.MessageBox.show({ title: 'loading' });
16319             Roo.MessageBox.show({
16320                title: "Please wait...",
16321                msg: msg,
16322                width:450,
16323                progress:true,
16324                closable:false,
16325                modal: false
16326               
16327             });
16328         }
16329         var total = mods.length;
16330         
16331         var _this = this;
16332         var progressRun = function() {
16333             if (!mods.length) {
16334                 Roo.debug && Roo.log('hide?');
16335                 if (!this.hideProgress && Roo.MessageBox) {
16336                     Roo.MessageBox.hide();
16337                 }
16338                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16339                 
16340                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16341                 
16342                 // THE END...
16343                 return false;   
16344             }
16345             
16346             var m = mods.shift();
16347             
16348             
16349             Roo.debug && Roo.log(m);
16350             // not sure if this is supported any more.. - modules that are are just function
16351             if (typeof(m) == 'function') { 
16352                 m.call(this);
16353                 return progressRun.defer(10, _this);
16354             } 
16355             
16356             
16357             msg = "Building Interface " + (total  - mods.length) + 
16358                     " of " + total + 
16359                     (m.name ? (' - ' + m.name) : '');
16360                         Roo.debug && Roo.log(msg);
16361             if (!this.hideProgress &&  Roo.MessageBox) { 
16362                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16363             }
16364             
16365          
16366             // is the module disabled?
16367             var disabled = (typeof(m.disabled) == 'function') ?
16368                 m.disabled.call(m.module.disabled) : m.disabled;    
16369             
16370             
16371             if (disabled) {
16372                 return progressRun(); // we do not update the display!
16373             }
16374             
16375             // now build 
16376             
16377                         
16378                         
16379             m.render();
16380             // it's 10 on top level, and 1 on others??? why...
16381             return progressRun.defer(10, _this);
16382              
16383         }
16384         progressRun.defer(1, _this);
16385      
16386         
16387         
16388     },
16389         
16390         
16391         /**
16392          * Event Object.
16393          *
16394          *
16395          */
16396         event: false, 
16397     /**
16398          * wrapper for event.on - aliased later..  
16399          * Typically use to register a event handler for register:
16400          *
16401          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16402          *
16403          */
16404     on : false
16405    
16406     
16407     
16408 });
16409
16410 Roo.XComponent.event = new Roo.util.Observable({
16411                 events : { 
16412                         /**
16413                          * @event register
16414                          * Fires when an Component is registered,
16415                          * set the disable property on the Component to stop registration.
16416                          * @param {Roo.XComponent} c the component being registerd.
16417                          * 
16418                          */
16419                         'register' : true,
16420             /**
16421                          * @event beforebuild
16422                          * Fires before each Component is built
16423                          * can be used to apply permissions.
16424                          * @param {Roo.XComponent} c the component being registerd.
16425                          * 
16426                          */
16427                         'beforebuild' : true,
16428                         /**
16429                          * @event buildcomplete
16430                          * Fires on the top level element when all elements have been built
16431                          * @param {Roo.XComponent} the top level component.
16432                          */
16433                         'buildcomplete' : true
16434                         
16435                 }
16436 });
16437
16438 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16439  /*
16440  * Based on:
16441  * Ext JS Library 1.1.1
16442  * Copyright(c) 2006-2007, Ext JS, LLC.
16443  *
16444  * Originally Released Under LGPL - original licence link has changed is not relivant.
16445  *
16446  * Fork - LGPL
16447  * <script type="text/javascript">
16448  */
16449
16450
16451
16452 /*
16453  * These classes are derivatives of the similarly named classes in the YUI Library.
16454  * The original license:
16455  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16456  * Code licensed under the BSD License:
16457  * http://developer.yahoo.net/yui/license.txt
16458  */
16459
16460 (function() {
16461
16462 var Event=Roo.EventManager;
16463 var Dom=Roo.lib.Dom;
16464
16465 /**
16466  * @class Roo.dd.DragDrop
16467  * @extends Roo.util.Observable
16468  * Defines the interface and base operation of items that that can be
16469  * dragged or can be drop targets.  It was designed to be extended, overriding
16470  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16471  * Up to three html elements can be associated with a DragDrop instance:
16472  * <ul>
16473  * <li>linked element: the element that is passed into the constructor.
16474  * This is the element which defines the boundaries for interaction with
16475  * other DragDrop objects.</li>
16476  * <li>handle element(s): The drag operation only occurs if the element that
16477  * was clicked matches a handle element.  By default this is the linked
16478  * element, but there are times that you will want only a portion of the
16479  * linked element to initiate the drag operation, and the setHandleElId()
16480  * method provides a way to define this.</li>
16481  * <li>drag element: this represents the element that would be moved along
16482  * with the cursor during a drag operation.  By default, this is the linked
16483  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16484  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16485  * </li>
16486  * </ul>
16487  * This class should not be instantiated until the onload event to ensure that
16488  * the associated elements are available.
16489  * The following would define a DragDrop obj that would interact with any
16490  * other DragDrop obj in the "group1" group:
16491  * <pre>
16492  *  dd = new Roo.dd.DragDrop("div1", "group1");
16493  * </pre>
16494  * Since none of the event handlers have been implemented, nothing would
16495  * actually happen if you were to run the code above.  Normally you would
16496  * override this class or one of the default implementations, but you can
16497  * also override the methods you want on an instance of the class...
16498  * <pre>
16499  *  dd.onDragDrop = function(e, id) {
16500  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16501  *  }
16502  * </pre>
16503  * @constructor
16504  * @param {String} id of the element that is linked to this instance
16505  * @param {String} sGroup the group of related DragDrop objects
16506  * @param {object} config an object containing configurable attributes
16507  *                Valid properties for DragDrop:
16508  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16509  */
16510 Roo.dd.DragDrop = function(id, sGroup, config) {
16511     if (id) {
16512         this.init(id, sGroup, config);
16513     }
16514     
16515 };
16516
16517 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16518
16519     /**
16520      * The id of the element associated with this object.  This is what we
16521      * refer to as the "linked element" because the size and position of
16522      * this element is used to determine when the drag and drop objects have
16523      * interacted.
16524      * @property id
16525      * @type String
16526      */
16527     id: null,
16528
16529     /**
16530      * Configuration attributes passed into the constructor
16531      * @property config
16532      * @type object
16533      */
16534     config: null,
16535
16536     /**
16537      * The id of the element that will be dragged.  By default this is same
16538      * as the linked element , but could be changed to another element. Ex:
16539      * Roo.dd.DDProxy
16540      * @property dragElId
16541      * @type String
16542      * @private
16543      */
16544     dragElId: null,
16545
16546     /**
16547      * the id of the element that initiates the drag operation.  By default
16548      * this is the linked element, but could be changed to be a child of this
16549      * element.  This lets us do things like only starting the drag when the
16550      * header element within the linked html element is clicked.
16551      * @property handleElId
16552      * @type String
16553      * @private
16554      */
16555     handleElId: null,
16556
16557     /**
16558      * An associative array of HTML tags that will be ignored if clicked.
16559      * @property invalidHandleTypes
16560      * @type {string: string}
16561      */
16562     invalidHandleTypes: null,
16563
16564     /**
16565      * An associative array of ids for elements that will be ignored if clicked
16566      * @property invalidHandleIds
16567      * @type {string: string}
16568      */
16569     invalidHandleIds: null,
16570
16571     /**
16572      * An indexted array of css class names for elements that will be ignored
16573      * if clicked.
16574      * @property invalidHandleClasses
16575      * @type string[]
16576      */
16577     invalidHandleClasses: null,
16578
16579     /**
16580      * The linked element's absolute X position at the time the drag was
16581      * started
16582      * @property startPageX
16583      * @type int
16584      * @private
16585      */
16586     startPageX: 0,
16587
16588     /**
16589      * The linked element's absolute X position at the time the drag was
16590      * started
16591      * @property startPageY
16592      * @type int
16593      * @private
16594      */
16595     startPageY: 0,
16596
16597     /**
16598      * The group defines a logical collection of DragDrop objects that are
16599      * related.  Instances only get events when interacting with other
16600      * DragDrop object in the same group.  This lets us define multiple
16601      * groups using a single DragDrop subclass if we want.
16602      * @property groups
16603      * @type {string: string}
16604      */
16605     groups: null,
16606
16607     /**
16608      * Individual drag/drop instances can be locked.  This will prevent
16609      * onmousedown start drag.
16610      * @property locked
16611      * @type boolean
16612      * @private
16613      */
16614     locked: false,
16615
16616     /**
16617      * Lock this instance
16618      * @method lock
16619      */
16620     lock: function() { this.locked = true; },
16621
16622     /**
16623      * Unlock this instace
16624      * @method unlock
16625      */
16626     unlock: function() { this.locked = false; },
16627
16628     /**
16629      * By default, all insances can be a drop target.  This can be disabled by
16630      * setting isTarget to false.
16631      * @method isTarget
16632      * @type boolean
16633      */
16634     isTarget: true,
16635
16636     /**
16637      * The padding configured for this drag and drop object for calculating
16638      * the drop zone intersection with this object.
16639      * @method padding
16640      * @type int[]
16641      */
16642     padding: null,
16643
16644     /**
16645      * Cached reference to the linked element
16646      * @property _domRef
16647      * @private
16648      */
16649     _domRef: null,
16650
16651     /**
16652      * Internal typeof flag
16653      * @property __ygDragDrop
16654      * @private
16655      */
16656     __ygDragDrop: true,
16657
16658     /**
16659      * Set to true when horizontal contraints are applied
16660      * @property constrainX
16661      * @type boolean
16662      * @private
16663      */
16664     constrainX: false,
16665
16666     /**
16667      * Set to true when vertical contraints are applied
16668      * @property constrainY
16669      * @type boolean
16670      * @private
16671      */
16672     constrainY: false,
16673
16674     /**
16675      * The left constraint
16676      * @property minX
16677      * @type int
16678      * @private
16679      */
16680     minX: 0,
16681
16682     /**
16683      * The right constraint
16684      * @property maxX
16685      * @type int
16686      * @private
16687      */
16688     maxX: 0,
16689
16690     /**
16691      * The up constraint
16692      * @property minY
16693      * @type int
16694      * @type int
16695      * @private
16696      */
16697     minY: 0,
16698
16699     /**
16700      * The down constraint
16701      * @property maxY
16702      * @type int
16703      * @private
16704      */
16705     maxY: 0,
16706
16707     /**
16708      * Maintain offsets when we resetconstraints.  Set to true when you want
16709      * the position of the element relative to its parent to stay the same
16710      * when the page changes
16711      *
16712      * @property maintainOffset
16713      * @type boolean
16714      */
16715     maintainOffset: false,
16716
16717     /**
16718      * Array of pixel locations the element will snap to if we specified a
16719      * horizontal graduation/interval.  This array is generated automatically
16720      * when you define a tick interval.
16721      * @property xTicks
16722      * @type int[]
16723      */
16724     xTicks: null,
16725
16726     /**
16727      * Array of pixel locations the element will snap to if we specified a
16728      * vertical graduation/interval.  This array is generated automatically
16729      * when you define a tick interval.
16730      * @property yTicks
16731      * @type int[]
16732      */
16733     yTicks: null,
16734
16735     /**
16736      * By default the drag and drop instance will only respond to the primary
16737      * button click (left button for a right-handed mouse).  Set to true to
16738      * allow drag and drop to start with any mouse click that is propogated
16739      * by the browser
16740      * @property primaryButtonOnly
16741      * @type boolean
16742      */
16743     primaryButtonOnly: true,
16744
16745     /**
16746      * The availabe property is false until the linked dom element is accessible.
16747      * @property available
16748      * @type boolean
16749      */
16750     available: false,
16751
16752     /**
16753      * By default, drags can only be initiated if the mousedown occurs in the
16754      * region the linked element is.  This is done in part to work around a
16755      * bug in some browsers that mis-report the mousedown if the previous
16756      * mouseup happened outside of the window.  This property is set to true
16757      * if outer handles are defined.
16758      *
16759      * @property hasOuterHandles
16760      * @type boolean
16761      * @default false
16762      */
16763     hasOuterHandles: false,
16764
16765     /**
16766      * Code that executes immediately before the startDrag event
16767      * @method b4StartDrag
16768      * @private
16769      */
16770     b4StartDrag: function(x, y) { },
16771
16772     /**
16773      * Abstract method called after a drag/drop object is clicked
16774      * and the drag or mousedown time thresholds have beeen met.
16775      * @method startDrag
16776      * @param {int} X click location
16777      * @param {int} Y click location
16778      */
16779     startDrag: function(x, y) { /* override this */ },
16780
16781     /**
16782      * Code that executes immediately before the onDrag event
16783      * @method b4Drag
16784      * @private
16785      */
16786     b4Drag: function(e) { },
16787
16788     /**
16789      * Abstract method called during the onMouseMove event while dragging an
16790      * object.
16791      * @method onDrag
16792      * @param {Event} e the mousemove event
16793      */
16794     onDrag: function(e) { /* override this */ },
16795
16796     /**
16797      * Abstract method called when this element fist begins hovering over
16798      * another DragDrop obj
16799      * @method onDragEnter
16800      * @param {Event} e the mousemove event
16801      * @param {String|DragDrop[]} id In POINT mode, the element
16802      * id this is hovering over.  In INTERSECT mode, an array of one or more
16803      * dragdrop items being hovered over.
16804      */
16805     onDragEnter: function(e, id) { /* override this */ },
16806
16807     /**
16808      * Code that executes immediately before the onDragOver event
16809      * @method b4DragOver
16810      * @private
16811      */
16812     b4DragOver: function(e) { },
16813
16814     /**
16815      * Abstract method called when this element is hovering over another
16816      * DragDrop obj
16817      * @method onDragOver
16818      * @param {Event} e the mousemove event
16819      * @param {String|DragDrop[]} id In POINT mode, the element
16820      * id this is hovering over.  In INTERSECT mode, an array of dd items
16821      * being hovered over.
16822      */
16823     onDragOver: function(e, id) { /* override this */ },
16824
16825     /**
16826      * Code that executes immediately before the onDragOut event
16827      * @method b4DragOut
16828      * @private
16829      */
16830     b4DragOut: function(e) { },
16831
16832     /**
16833      * Abstract method called when we are no longer hovering over an element
16834      * @method onDragOut
16835      * @param {Event} e the mousemove event
16836      * @param {String|DragDrop[]} id In POINT mode, the element
16837      * id this was hovering over.  In INTERSECT mode, an array of dd items
16838      * that the mouse is no longer over.
16839      */
16840     onDragOut: function(e, id) { /* override this */ },
16841
16842     /**
16843      * Code that executes immediately before the onDragDrop event
16844      * @method b4DragDrop
16845      * @private
16846      */
16847     b4DragDrop: function(e) { },
16848
16849     /**
16850      * Abstract method called when this item is dropped on another DragDrop
16851      * obj
16852      * @method onDragDrop
16853      * @param {Event} e the mouseup event
16854      * @param {String|DragDrop[]} id In POINT mode, the element
16855      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16856      * was dropped on.
16857      */
16858     onDragDrop: function(e, id) { /* override this */ },
16859
16860     /**
16861      * Abstract method called when this item is dropped on an area with no
16862      * drop target
16863      * @method onInvalidDrop
16864      * @param {Event} e the mouseup event
16865      */
16866     onInvalidDrop: function(e) { /* override this */ },
16867
16868     /**
16869      * Code that executes immediately before the endDrag event
16870      * @method b4EndDrag
16871      * @private
16872      */
16873     b4EndDrag: function(e) { },
16874
16875     /**
16876      * Fired when we are done dragging the object
16877      * @method endDrag
16878      * @param {Event} e the mouseup event
16879      */
16880     endDrag: function(e) { /* override this */ },
16881
16882     /**
16883      * Code executed immediately before the onMouseDown event
16884      * @method b4MouseDown
16885      * @param {Event} e the mousedown event
16886      * @private
16887      */
16888     b4MouseDown: function(e) {  },
16889
16890     /**
16891      * Event handler that fires when a drag/drop obj gets a mousedown
16892      * @method onMouseDown
16893      * @param {Event} e the mousedown event
16894      */
16895     onMouseDown: function(e) { /* override this */ },
16896
16897     /**
16898      * Event handler that fires when a drag/drop obj gets a mouseup
16899      * @method onMouseUp
16900      * @param {Event} e the mouseup event
16901      */
16902     onMouseUp: function(e) { /* override this */ },
16903
16904     /**
16905      * Override the onAvailable method to do what is needed after the initial
16906      * position was determined.
16907      * @method onAvailable
16908      */
16909     onAvailable: function () {
16910     },
16911
16912     /*
16913      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16914      * @type Object
16915      */
16916     defaultPadding : {left:0, right:0, top:0, bottom:0},
16917
16918     /*
16919      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16920  *
16921  * Usage:
16922  <pre><code>
16923  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16924                 { dragElId: "existingProxyDiv" });
16925  dd.startDrag = function(){
16926      this.constrainTo("parent-id");
16927  };
16928  </code></pre>
16929  * Or you can initalize it using the {@link Roo.Element} object:
16930  <pre><code>
16931  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16932      startDrag : function(){
16933          this.constrainTo("parent-id");
16934      }
16935  });
16936  </code></pre>
16937      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16938      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16939      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16940      * an object containing the sides to pad. For example: {right:10, bottom:10}
16941      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16942      */
16943     constrainTo : function(constrainTo, pad, inContent){
16944         if(typeof pad == "number"){
16945             pad = {left: pad, right:pad, top:pad, bottom:pad};
16946         }
16947         pad = pad || this.defaultPadding;
16948         var b = Roo.get(this.getEl()).getBox();
16949         var ce = Roo.get(constrainTo);
16950         var s = ce.getScroll();
16951         var c, cd = ce.dom;
16952         if(cd == document.body){
16953             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16954         }else{
16955             xy = ce.getXY();
16956             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16957         }
16958
16959
16960         var topSpace = b.y - c.y;
16961         var leftSpace = b.x - c.x;
16962
16963         this.resetConstraints();
16964         this.setXConstraint(leftSpace - (pad.left||0), // left
16965                 c.width - leftSpace - b.width - (pad.right||0) //right
16966         );
16967         this.setYConstraint(topSpace - (pad.top||0), //top
16968                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16969         );
16970     },
16971
16972     /**
16973      * Returns a reference to the linked element
16974      * @method getEl
16975      * @return {HTMLElement} the html element
16976      */
16977     getEl: function() {
16978         if (!this._domRef) {
16979             this._domRef = Roo.getDom(this.id);
16980         }
16981
16982         return this._domRef;
16983     },
16984
16985     /**
16986      * Returns a reference to the actual element to drag.  By default this is
16987      * the same as the html element, but it can be assigned to another
16988      * element. An example of this can be found in Roo.dd.DDProxy
16989      * @method getDragEl
16990      * @return {HTMLElement} the html element
16991      */
16992     getDragEl: function() {
16993         return Roo.getDom(this.dragElId);
16994     },
16995
16996     /**
16997      * Sets up the DragDrop object.  Must be called in the constructor of any
16998      * Roo.dd.DragDrop subclass
16999      * @method init
17000      * @param id the id of the linked element
17001      * @param {String} sGroup the group of related items
17002      * @param {object} config configuration attributes
17003      */
17004     init: function(id, sGroup, config) {
17005         this.initTarget(id, sGroup, config);
17006         if (!Roo.isTouch) {
17007             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17008         }
17009         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17010         // Event.on(this.id, "selectstart", Event.preventDefault);
17011     },
17012
17013     /**
17014      * Initializes Targeting functionality only... the object does not
17015      * get a mousedown handler.
17016      * @method initTarget
17017      * @param id the id of the linked element
17018      * @param {String} sGroup the group of related items
17019      * @param {object} config configuration attributes
17020      */
17021     initTarget: function(id, sGroup, config) {
17022
17023         // configuration attributes
17024         this.config = config || {};
17025
17026         // create a local reference to the drag and drop manager
17027         this.DDM = Roo.dd.DDM;
17028         // initialize the groups array
17029         this.groups = {};
17030
17031         // assume that we have an element reference instead of an id if the
17032         // parameter is not a string
17033         if (typeof id !== "string") {
17034             id = Roo.id(id);
17035         }
17036
17037         // set the id
17038         this.id = id;
17039
17040         // add to an interaction group
17041         this.addToGroup((sGroup) ? sGroup : "default");
17042
17043         // We don't want to register this as the handle with the manager
17044         // so we just set the id rather than calling the setter.
17045         this.handleElId = id;
17046
17047         // the linked element is the element that gets dragged by default
17048         this.setDragElId(id);
17049
17050         // by default, clicked anchors will not start drag operations.
17051         this.invalidHandleTypes = { A: "A" };
17052         this.invalidHandleIds = {};
17053         this.invalidHandleClasses = [];
17054
17055         this.applyConfig();
17056
17057         this.handleOnAvailable();
17058     },
17059
17060     /**
17061      * Applies the configuration parameters that were passed into the constructor.
17062      * This is supposed to happen at each level through the inheritance chain.  So
17063      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17064      * DragDrop in order to get all of the parameters that are available in
17065      * each object.
17066      * @method applyConfig
17067      */
17068     applyConfig: function() {
17069
17070         // configurable properties:
17071         //    padding, isTarget, maintainOffset, primaryButtonOnly
17072         this.padding           = this.config.padding || [0, 0, 0, 0];
17073         this.isTarget          = (this.config.isTarget !== false);
17074         this.maintainOffset    = (this.config.maintainOffset);
17075         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17076
17077     },
17078
17079     /**
17080      * Executed when the linked element is available
17081      * @method handleOnAvailable
17082      * @private
17083      */
17084     handleOnAvailable: function() {
17085         this.available = true;
17086         this.resetConstraints();
17087         this.onAvailable();
17088     },
17089
17090      /**
17091      * Configures the padding for the target zone in px.  Effectively expands
17092      * (or reduces) the virtual object size for targeting calculations.
17093      * Supports css-style shorthand; if only one parameter is passed, all sides
17094      * will have that padding, and if only two are passed, the top and bottom
17095      * will have the first param, the left and right the second.
17096      * @method setPadding
17097      * @param {int} iTop    Top pad
17098      * @param {int} iRight  Right pad
17099      * @param {int} iBot    Bot pad
17100      * @param {int} iLeft   Left pad
17101      */
17102     setPadding: function(iTop, iRight, iBot, iLeft) {
17103         // this.padding = [iLeft, iRight, iTop, iBot];
17104         if (!iRight && 0 !== iRight) {
17105             this.padding = [iTop, iTop, iTop, iTop];
17106         } else if (!iBot && 0 !== iBot) {
17107             this.padding = [iTop, iRight, iTop, iRight];
17108         } else {
17109             this.padding = [iTop, iRight, iBot, iLeft];
17110         }
17111     },
17112
17113     /**
17114      * Stores the initial placement of the linked element.
17115      * @method setInitialPosition
17116      * @param {int} diffX   the X offset, default 0
17117      * @param {int} diffY   the Y offset, default 0
17118      */
17119     setInitPosition: function(diffX, diffY) {
17120         var el = this.getEl();
17121
17122         if (!this.DDM.verifyEl(el)) {
17123             return;
17124         }
17125
17126         var dx = diffX || 0;
17127         var dy = diffY || 0;
17128
17129         var p = Dom.getXY( el );
17130
17131         this.initPageX = p[0] - dx;
17132         this.initPageY = p[1] - dy;
17133
17134         this.lastPageX = p[0];
17135         this.lastPageY = p[1];
17136
17137
17138         this.setStartPosition(p);
17139     },
17140
17141     /**
17142      * Sets the start position of the element.  This is set when the obj
17143      * is initialized, the reset when a drag is started.
17144      * @method setStartPosition
17145      * @param pos current position (from previous lookup)
17146      * @private
17147      */
17148     setStartPosition: function(pos) {
17149         var p = pos || Dom.getXY( this.getEl() );
17150         this.deltaSetXY = null;
17151
17152         this.startPageX = p[0];
17153         this.startPageY = p[1];
17154     },
17155
17156     /**
17157      * Add this instance to a group of related drag/drop objects.  All
17158      * instances belong to at least one group, and can belong to as many
17159      * groups as needed.
17160      * @method addToGroup
17161      * @param sGroup {string} the name of the group
17162      */
17163     addToGroup: function(sGroup) {
17164         this.groups[sGroup] = true;
17165         this.DDM.regDragDrop(this, sGroup);
17166     },
17167
17168     /**
17169      * Remove's this instance from the supplied interaction group
17170      * @method removeFromGroup
17171      * @param {string}  sGroup  The group to drop
17172      */
17173     removeFromGroup: function(sGroup) {
17174         if (this.groups[sGroup]) {
17175             delete this.groups[sGroup];
17176         }
17177
17178         this.DDM.removeDDFromGroup(this, sGroup);
17179     },
17180
17181     /**
17182      * Allows you to specify that an element other than the linked element
17183      * will be moved with the cursor during a drag
17184      * @method setDragElId
17185      * @param id {string} the id of the element that will be used to initiate the drag
17186      */
17187     setDragElId: function(id) {
17188         this.dragElId = id;
17189     },
17190
17191     /**
17192      * Allows you to specify a child of the linked element that should be
17193      * used to initiate the drag operation.  An example of this would be if
17194      * you have a content div with text and links.  Clicking anywhere in the
17195      * content area would normally start the drag operation.  Use this method
17196      * to specify that an element inside of the content div is the element
17197      * that starts the drag operation.
17198      * @method setHandleElId
17199      * @param id {string} the id of the element that will be used to
17200      * initiate the drag.
17201      */
17202     setHandleElId: function(id) {
17203         if (typeof id !== "string") {
17204             id = Roo.id(id);
17205         }
17206         this.handleElId = id;
17207         this.DDM.regHandle(this.id, id);
17208     },
17209
17210     /**
17211      * Allows you to set an element outside of the linked element as a drag
17212      * handle
17213      * @method setOuterHandleElId
17214      * @param id the id of the element that will be used to initiate the drag
17215      */
17216     setOuterHandleElId: function(id) {
17217         if (typeof id !== "string") {
17218             id = Roo.id(id);
17219         }
17220         Event.on(id, "mousedown",
17221                 this.handleMouseDown, this);
17222         this.setHandleElId(id);
17223
17224         this.hasOuterHandles = true;
17225     },
17226
17227     /**
17228      * Remove all drag and drop hooks for this element
17229      * @method unreg
17230      */
17231     unreg: function() {
17232         Event.un(this.id, "mousedown",
17233                 this.handleMouseDown);
17234         Event.un(this.id, "touchstart",
17235                 this.handleMouseDown);
17236         this._domRef = null;
17237         this.DDM._remove(this);
17238     },
17239
17240     destroy : function(){
17241         this.unreg();
17242     },
17243
17244     /**
17245      * Returns true if this instance is locked, or the drag drop mgr is locked
17246      * (meaning that all drag/drop is disabled on the page.)
17247      * @method isLocked
17248      * @return {boolean} true if this obj or all drag/drop is locked, else
17249      * false
17250      */
17251     isLocked: function() {
17252         return (this.DDM.isLocked() || this.locked);
17253     },
17254
17255     /**
17256      * Fired when this object is clicked
17257      * @method handleMouseDown
17258      * @param {Event} e
17259      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17260      * @private
17261      */
17262     handleMouseDown: function(e, oDD){
17263      
17264         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17265             //Roo.log('not touch/ button !=0');
17266             return;
17267         }
17268         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17269             return; // double touch..
17270         }
17271         
17272
17273         if (this.isLocked()) {
17274             //Roo.log('locked');
17275             return;
17276         }
17277
17278         this.DDM.refreshCache(this.groups);
17279 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17280         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17281         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17282             //Roo.log('no outer handes or not over target');
17283                 // do nothing.
17284         } else {
17285 //            Roo.log('check validator');
17286             if (this.clickValidator(e)) {
17287 //                Roo.log('validate success');
17288                 // set the initial element position
17289                 this.setStartPosition();
17290
17291
17292                 this.b4MouseDown(e);
17293                 this.onMouseDown(e);
17294
17295                 this.DDM.handleMouseDown(e, this);
17296
17297                 this.DDM.stopEvent(e);
17298             } else {
17299
17300
17301             }
17302         }
17303     },
17304
17305     clickValidator: function(e) {
17306         var target = e.getTarget();
17307         return ( this.isValidHandleChild(target) &&
17308                     (this.id == this.handleElId ||
17309                         this.DDM.handleWasClicked(target, this.id)) );
17310     },
17311
17312     /**
17313      * Allows you to specify a tag name that should not start a drag operation
17314      * when clicked.  This is designed to facilitate embedding links within a
17315      * drag handle that do something other than start the drag.
17316      * @method addInvalidHandleType
17317      * @param {string} tagName the type of element to exclude
17318      */
17319     addInvalidHandleType: function(tagName) {
17320         var type = tagName.toUpperCase();
17321         this.invalidHandleTypes[type] = type;
17322     },
17323
17324     /**
17325      * Lets you to specify an element id for a child of a drag handle
17326      * that should not initiate a drag
17327      * @method addInvalidHandleId
17328      * @param {string} id the element id of the element you wish to ignore
17329      */
17330     addInvalidHandleId: function(id) {
17331         if (typeof id !== "string") {
17332             id = Roo.id(id);
17333         }
17334         this.invalidHandleIds[id] = id;
17335     },
17336
17337     /**
17338      * Lets you specify a css class of elements that will not initiate a drag
17339      * @method addInvalidHandleClass
17340      * @param {string} cssClass the class of the elements you wish to ignore
17341      */
17342     addInvalidHandleClass: function(cssClass) {
17343         this.invalidHandleClasses.push(cssClass);
17344     },
17345
17346     /**
17347      * Unsets an excluded tag name set by addInvalidHandleType
17348      * @method removeInvalidHandleType
17349      * @param {string} tagName the type of element to unexclude
17350      */
17351     removeInvalidHandleType: function(tagName) {
17352         var type = tagName.toUpperCase();
17353         // this.invalidHandleTypes[type] = null;
17354         delete this.invalidHandleTypes[type];
17355     },
17356
17357     /**
17358      * Unsets an invalid handle id
17359      * @method removeInvalidHandleId
17360      * @param {string} id the id of the element to re-enable
17361      */
17362     removeInvalidHandleId: function(id) {
17363         if (typeof id !== "string") {
17364             id = Roo.id(id);
17365         }
17366         delete this.invalidHandleIds[id];
17367     },
17368
17369     /**
17370      * Unsets an invalid css class
17371      * @method removeInvalidHandleClass
17372      * @param {string} cssClass the class of the element(s) you wish to
17373      * re-enable
17374      */
17375     removeInvalidHandleClass: function(cssClass) {
17376         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17377             if (this.invalidHandleClasses[i] == cssClass) {
17378                 delete this.invalidHandleClasses[i];
17379             }
17380         }
17381     },
17382
17383     /**
17384      * Checks the tag exclusion list to see if this click should be ignored
17385      * @method isValidHandleChild
17386      * @param {HTMLElement} node the HTMLElement to evaluate
17387      * @return {boolean} true if this is a valid tag type, false if not
17388      */
17389     isValidHandleChild: function(node) {
17390
17391         var valid = true;
17392         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17393         var nodeName;
17394         try {
17395             nodeName = node.nodeName.toUpperCase();
17396         } catch(e) {
17397             nodeName = node.nodeName;
17398         }
17399         valid = valid && !this.invalidHandleTypes[nodeName];
17400         valid = valid && !this.invalidHandleIds[node.id];
17401
17402         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17403             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17404         }
17405
17406
17407         return valid;
17408
17409     },
17410
17411     /**
17412      * Create the array of horizontal tick marks if an interval was specified
17413      * in setXConstraint().
17414      * @method setXTicks
17415      * @private
17416      */
17417     setXTicks: function(iStartX, iTickSize) {
17418         this.xTicks = [];
17419         this.xTickSize = iTickSize;
17420
17421         var tickMap = {};
17422
17423         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17424             if (!tickMap[i]) {
17425                 this.xTicks[this.xTicks.length] = i;
17426                 tickMap[i] = true;
17427             }
17428         }
17429
17430         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17431             if (!tickMap[i]) {
17432                 this.xTicks[this.xTicks.length] = i;
17433                 tickMap[i] = true;
17434             }
17435         }
17436
17437         this.xTicks.sort(this.DDM.numericSort) ;
17438     },
17439
17440     /**
17441      * Create the array of vertical tick marks if an interval was specified in
17442      * setYConstraint().
17443      * @method setYTicks
17444      * @private
17445      */
17446     setYTicks: function(iStartY, iTickSize) {
17447         this.yTicks = [];
17448         this.yTickSize = iTickSize;
17449
17450         var tickMap = {};
17451
17452         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17453             if (!tickMap[i]) {
17454                 this.yTicks[this.yTicks.length] = i;
17455                 tickMap[i] = true;
17456             }
17457         }
17458
17459         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17460             if (!tickMap[i]) {
17461                 this.yTicks[this.yTicks.length] = i;
17462                 tickMap[i] = true;
17463             }
17464         }
17465
17466         this.yTicks.sort(this.DDM.numericSort) ;
17467     },
17468
17469     /**
17470      * By default, the element can be dragged any place on the screen.  Use
17471      * this method to limit the horizontal travel of the element.  Pass in
17472      * 0,0 for the parameters if you want to lock the drag to the y axis.
17473      * @method setXConstraint
17474      * @param {int} iLeft the number of pixels the element can move to the left
17475      * @param {int} iRight the number of pixels the element can move to the
17476      * right
17477      * @param {int} iTickSize optional parameter for specifying that the
17478      * element
17479      * should move iTickSize pixels at a time.
17480      */
17481     setXConstraint: function(iLeft, iRight, iTickSize) {
17482         this.leftConstraint = iLeft;
17483         this.rightConstraint = iRight;
17484
17485         this.minX = this.initPageX - iLeft;
17486         this.maxX = this.initPageX + iRight;
17487         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17488
17489         this.constrainX = true;
17490     },
17491
17492     /**
17493      * Clears any constraints applied to this instance.  Also clears ticks
17494      * since they can't exist independent of a constraint at this time.
17495      * @method clearConstraints
17496      */
17497     clearConstraints: function() {
17498         this.constrainX = false;
17499         this.constrainY = false;
17500         this.clearTicks();
17501     },
17502
17503     /**
17504      * Clears any tick interval defined for this instance
17505      * @method clearTicks
17506      */
17507     clearTicks: function() {
17508         this.xTicks = null;
17509         this.yTicks = null;
17510         this.xTickSize = 0;
17511         this.yTickSize = 0;
17512     },
17513
17514     /**
17515      * By default, the element can be dragged any place on the screen.  Set
17516      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17517      * parameters if you want to lock the drag to the x axis.
17518      * @method setYConstraint
17519      * @param {int} iUp the number of pixels the element can move up
17520      * @param {int} iDown the number of pixels the element can move down
17521      * @param {int} iTickSize optional parameter for specifying that the
17522      * element should move iTickSize pixels at a time.
17523      */
17524     setYConstraint: function(iUp, iDown, iTickSize) {
17525         this.topConstraint = iUp;
17526         this.bottomConstraint = iDown;
17527
17528         this.minY = this.initPageY - iUp;
17529         this.maxY = this.initPageY + iDown;
17530         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17531
17532         this.constrainY = true;
17533
17534     },
17535
17536     /**
17537      * resetConstraints must be called if you manually reposition a dd element.
17538      * @method resetConstraints
17539      * @param {boolean} maintainOffset
17540      */
17541     resetConstraints: function() {
17542
17543
17544         // Maintain offsets if necessary
17545         if (this.initPageX || this.initPageX === 0) {
17546             // figure out how much this thing has moved
17547             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17548             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17549
17550             this.setInitPosition(dx, dy);
17551
17552         // This is the first time we have detected the element's position
17553         } else {
17554             this.setInitPosition();
17555         }
17556
17557         if (this.constrainX) {
17558             this.setXConstraint( this.leftConstraint,
17559                                  this.rightConstraint,
17560                                  this.xTickSize        );
17561         }
17562
17563         if (this.constrainY) {
17564             this.setYConstraint( this.topConstraint,
17565                                  this.bottomConstraint,
17566                                  this.yTickSize         );
17567         }
17568     },
17569
17570     /**
17571      * Normally the drag element is moved pixel by pixel, but we can specify
17572      * that it move a number of pixels at a time.  This method resolves the
17573      * location when we have it set up like this.
17574      * @method getTick
17575      * @param {int} val where we want to place the object
17576      * @param {int[]} tickArray sorted array of valid points
17577      * @return {int} the closest tick
17578      * @private
17579      */
17580     getTick: function(val, tickArray) {
17581
17582         if (!tickArray) {
17583             // If tick interval is not defined, it is effectively 1 pixel,
17584             // so we return the value passed to us.
17585             return val;
17586         } else if (tickArray[0] >= val) {
17587             // The value is lower than the first tick, so we return the first
17588             // tick.
17589             return tickArray[0];
17590         } else {
17591             for (var i=0, len=tickArray.length; i<len; ++i) {
17592                 var next = i + 1;
17593                 if (tickArray[next] && tickArray[next] >= val) {
17594                     var diff1 = val - tickArray[i];
17595                     var diff2 = tickArray[next] - val;
17596                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17597                 }
17598             }
17599
17600             // The value is larger than the last tick, so we return the last
17601             // tick.
17602             return tickArray[tickArray.length - 1];
17603         }
17604     },
17605
17606     /**
17607      * toString method
17608      * @method toString
17609      * @return {string} string representation of the dd obj
17610      */
17611     toString: function() {
17612         return ("DragDrop " + this.id);
17613     }
17614
17615 });
17616
17617 })();
17618 /*
17619  * Based on:
17620  * Ext JS Library 1.1.1
17621  * Copyright(c) 2006-2007, Ext JS, LLC.
17622  *
17623  * Originally Released Under LGPL - original licence link has changed is not relivant.
17624  *
17625  * Fork - LGPL
17626  * <script type="text/javascript">
17627  */
17628
17629
17630 /**
17631  * The drag and drop utility provides a framework for building drag and drop
17632  * applications.  In addition to enabling drag and drop for specific elements,
17633  * the drag and drop elements are tracked by the manager class, and the
17634  * interactions between the various elements are tracked during the drag and
17635  * the implementing code is notified about these important moments.
17636  */
17637
17638 // Only load the library once.  Rewriting the manager class would orphan
17639 // existing drag and drop instances.
17640 if (!Roo.dd.DragDropMgr) {
17641
17642 /**
17643  * @class Roo.dd.DragDropMgr
17644  * DragDropMgr is a singleton that tracks the element interaction for
17645  * all DragDrop items in the window.  Generally, you will not call
17646  * this class directly, but it does have helper methods that could
17647  * be useful in your DragDrop implementations.
17648  * @singleton
17649  */
17650 Roo.dd.DragDropMgr = function() {
17651
17652     var Event = Roo.EventManager;
17653
17654     return {
17655
17656         /**
17657          * Two dimensional Array of registered DragDrop objects.  The first
17658          * dimension is the DragDrop item group, the second the DragDrop
17659          * object.
17660          * @property ids
17661          * @type {string: string}
17662          * @private
17663          * @static
17664          */
17665         ids: {},
17666
17667         /**
17668          * Array of element ids defined as drag handles.  Used to determine
17669          * if the element that generated the mousedown event is actually the
17670          * handle and not the html element itself.
17671          * @property handleIds
17672          * @type {string: string}
17673          * @private
17674          * @static
17675          */
17676         handleIds: {},
17677
17678         /**
17679          * the DragDrop object that is currently being dragged
17680          * @property dragCurrent
17681          * @type DragDrop
17682          * @private
17683          * @static
17684          **/
17685         dragCurrent: null,
17686
17687         /**
17688          * the DragDrop object(s) that are being hovered over
17689          * @property dragOvers
17690          * @type Array
17691          * @private
17692          * @static
17693          */
17694         dragOvers: {},
17695
17696         /**
17697          * the X distance between the cursor and the object being dragged
17698          * @property deltaX
17699          * @type int
17700          * @private
17701          * @static
17702          */
17703         deltaX: 0,
17704
17705         /**
17706          * the Y distance between the cursor and the object being dragged
17707          * @property deltaY
17708          * @type int
17709          * @private
17710          * @static
17711          */
17712         deltaY: 0,
17713
17714         /**
17715          * Flag to determine if we should prevent the default behavior of the
17716          * events we define. By default this is true, but this can be set to
17717          * false if you need the default behavior (not recommended)
17718          * @property preventDefault
17719          * @type boolean
17720          * @static
17721          */
17722         preventDefault: true,
17723
17724         /**
17725          * Flag to determine if we should stop the propagation of the events
17726          * we generate. This is true by default but you may want to set it to
17727          * false if the html element contains other features that require the
17728          * mouse click.
17729          * @property stopPropagation
17730          * @type boolean
17731          * @static
17732          */
17733         stopPropagation: true,
17734
17735         /**
17736          * Internal flag that is set to true when drag and drop has been
17737          * intialized
17738          * @property initialized
17739          * @private
17740          * @static
17741          */
17742         initalized: false,
17743
17744         /**
17745          * All drag and drop can be disabled.
17746          * @property locked
17747          * @private
17748          * @static
17749          */
17750         locked: false,
17751
17752         /**
17753          * Called the first time an element is registered.
17754          * @method init
17755          * @private
17756          * @static
17757          */
17758         init: function() {
17759             this.initialized = true;
17760         },
17761
17762         /**
17763          * In point mode, drag and drop interaction is defined by the
17764          * location of the cursor during the drag/drop
17765          * @property POINT
17766          * @type int
17767          * @static
17768          */
17769         POINT: 0,
17770
17771         /**
17772          * In intersect mode, drag and drop interactio nis defined by the
17773          * overlap of two or more drag and drop objects.
17774          * @property INTERSECT
17775          * @type int
17776          * @static
17777          */
17778         INTERSECT: 1,
17779
17780         /**
17781          * The current drag and drop mode.  Default: POINT
17782          * @property mode
17783          * @type int
17784          * @static
17785          */
17786         mode: 0,
17787
17788         /**
17789          * Runs method on all drag and drop objects
17790          * @method _execOnAll
17791          * @private
17792          * @static
17793          */
17794         _execOnAll: function(sMethod, args) {
17795             for (var i in this.ids) {
17796                 for (var j in this.ids[i]) {
17797                     var oDD = this.ids[i][j];
17798                     if (! this.isTypeOfDD(oDD)) {
17799                         continue;
17800                     }
17801                     oDD[sMethod].apply(oDD, args);
17802                 }
17803             }
17804         },
17805
17806         /**
17807          * Drag and drop initialization.  Sets up the global event handlers
17808          * @method _onLoad
17809          * @private
17810          * @static
17811          */
17812         _onLoad: function() {
17813
17814             this.init();
17815
17816             if (!Roo.isTouch) {
17817                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17818                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17819             }
17820             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17821             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17822             
17823             Event.on(window,   "unload",    this._onUnload, this, true);
17824             Event.on(window,   "resize",    this._onResize, this, true);
17825             // Event.on(window,   "mouseout",    this._test);
17826
17827         },
17828
17829         /**
17830          * Reset constraints on all drag and drop objs
17831          * @method _onResize
17832          * @private
17833          * @static
17834          */
17835         _onResize: function(e) {
17836             this._execOnAll("resetConstraints", []);
17837         },
17838
17839         /**
17840          * Lock all drag and drop functionality
17841          * @method lock
17842          * @static
17843          */
17844         lock: function() { this.locked = true; },
17845
17846         /**
17847          * Unlock all drag and drop functionality
17848          * @method unlock
17849          * @static
17850          */
17851         unlock: function() { this.locked = false; },
17852
17853         /**
17854          * Is drag and drop locked?
17855          * @method isLocked
17856          * @return {boolean} True if drag and drop is locked, false otherwise.
17857          * @static
17858          */
17859         isLocked: function() { return this.locked; },
17860
17861         /**
17862          * Location cache that is set for all drag drop objects when a drag is
17863          * initiated, cleared when the drag is finished.
17864          * @property locationCache
17865          * @private
17866          * @static
17867          */
17868         locationCache: {},
17869
17870         /**
17871          * Set useCache to false if you want to force object the lookup of each
17872          * drag and drop linked element constantly during a drag.
17873          * @property useCache
17874          * @type boolean
17875          * @static
17876          */
17877         useCache: true,
17878
17879         /**
17880          * The number of pixels that the mouse needs to move after the
17881          * mousedown before the drag is initiated.  Default=3;
17882          * @property clickPixelThresh
17883          * @type int
17884          * @static
17885          */
17886         clickPixelThresh: 3,
17887
17888         /**
17889          * The number of milliseconds after the mousedown event to initiate the
17890          * drag if we don't get a mouseup event. Default=1000
17891          * @property clickTimeThresh
17892          * @type int
17893          * @static
17894          */
17895         clickTimeThresh: 350,
17896
17897         /**
17898          * Flag that indicates that either the drag pixel threshold or the
17899          * mousdown time threshold has been met
17900          * @property dragThreshMet
17901          * @type boolean
17902          * @private
17903          * @static
17904          */
17905         dragThreshMet: false,
17906
17907         /**
17908          * Timeout used for the click time threshold
17909          * @property clickTimeout
17910          * @type Object
17911          * @private
17912          * @static
17913          */
17914         clickTimeout: null,
17915
17916         /**
17917          * The X position of the mousedown event stored for later use when a
17918          * drag threshold is met.
17919          * @property startX
17920          * @type int
17921          * @private
17922          * @static
17923          */
17924         startX: 0,
17925
17926         /**
17927          * The Y position of the mousedown event stored for later use when a
17928          * drag threshold is met.
17929          * @property startY
17930          * @type int
17931          * @private
17932          * @static
17933          */
17934         startY: 0,
17935
17936         /**
17937          * Each DragDrop instance must be registered with the DragDropMgr.
17938          * This is executed in DragDrop.init()
17939          * @method regDragDrop
17940          * @param {DragDrop} oDD the DragDrop object to register
17941          * @param {String} sGroup the name of the group this element belongs to
17942          * @static
17943          */
17944         regDragDrop: function(oDD, sGroup) {
17945             if (!this.initialized) { this.init(); }
17946
17947             if (!this.ids[sGroup]) {
17948                 this.ids[sGroup] = {};
17949             }
17950             this.ids[sGroup][oDD.id] = oDD;
17951         },
17952
17953         /**
17954          * Removes the supplied dd instance from the supplied group. Executed
17955          * by DragDrop.removeFromGroup, so don't call this function directly.
17956          * @method removeDDFromGroup
17957          * @private
17958          * @static
17959          */
17960         removeDDFromGroup: function(oDD, sGroup) {
17961             if (!this.ids[sGroup]) {
17962                 this.ids[sGroup] = {};
17963             }
17964
17965             var obj = this.ids[sGroup];
17966             if (obj && obj[oDD.id]) {
17967                 delete obj[oDD.id];
17968             }
17969         },
17970
17971         /**
17972          * Unregisters a drag and drop item.  This is executed in
17973          * DragDrop.unreg, use that method instead of calling this directly.
17974          * @method _remove
17975          * @private
17976          * @static
17977          */
17978         _remove: function(oDD) {
17979             for (var g in oDD.groups) {
17980                 if (g && this.ids[g][oDD.id]) {
17981                     delete this.ids[g][oDD.id];
17982                 }
17983             }
17984             delete this.handleIds[oDD.id];
17985         },
17986
17987         /**
17988          * Each DragDrop handle element must be registered.  This is done
17989          * automatically when executing DragDrop.setHandleElId()
17990          * @method regHandle
17991          * @param {String} sDDId the DragDrop id this element is a handle for
17992          * @param {String} sHandleId the id of the element that is the drag
17993          * handle
17994          * @static
17995          */
17996         regHandle: function(sDDId, sHandleId) {
17997             if (!this.handleIds[sDDId]) {
17998                 this.handleIds[sDDId] = {};
17999             }
18000             this.handleIds[sDDId][sHandleId] = sHandleId;
18001         },
18002
18003         /**
18004          * Utility function to determine if a given element has been
18005          * registered as a drag drop item.
18006          * @method isDragDrop
18007          * @param {String} id the element id to check
18008          * @return {boolean} true if this element is a DragDrop item,
18009          * false otherwise
18010          * @static
18011          */
18012         isDragDrop: function(id) {
18013             return ( this.getDDById(id) ) ? true : false;
18014         },
18015
18016         /**
18017          * Returns the drag and drop instances that are in all groups the
18018          * passed in instance belongs to.
18019          * @method getRelated
18020          * @param {DragDrop} p_oDD the obj to get related data for
18021          * @param {boolean} bTargetsOnly if true, only return targetable objs
18022          * @return {DragDrop[]} the related instances
18023          * @static
18024          */
18025         getRelated: function(p_oDD, bTargetsOnly) {
18026             var oDDs = [];
18027             for (var i in p_oDD.groups) {
18028                 for (j in this.ids[i]) {
18029                     var dd = this.ids[i][j];
18030                     if (! this.isTypeOfDD(dd)) {
18031                         continue;
18032                     }
18033                     if (!bTargetsOnly || dd.isTarget) {
18034                         oDDs[oDDs.length] = dd;
18035                     }
18036                 }
18037             }
18038
18039             return oDDs;
18040         },
18041
18042         /**
18043          * Returns true if the specified dd target is a legal target for
18044          * the specifice drag obj
18045          * @method isLegalTarget
18046          * @param {DragDrop} the drag obj
18047          * @param {DragDrop} the target
18048          * @return {boolean} true if the target is a legal target for the
18049          * dd obj
18050          * @static
18051          */
18052         isLegalTarget: function (oDD, oTargetDD) {
18053             var targets = this.getRelated(oDD, true);
18054             for (var i=0, len=targets.length;i<len;++i) {
18055                 if (targets[i].id == oTargetDD.id) {
18056                     return true;
18057                 }
18058             }
18059
18060             return false;
18061         },
18062
18063         /**
18064          * My goal is to be able to transparently determine if an object is
18065          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18066          * returns "object", oDD.constructor.toString() always returns
18067          * "DragDrop" and not the name of the subclass.  So for now it just
18068          * evaluates a well-known variable in DragDrop.
18069          * @method isTypeOfDD
18070          * @param {Object} the object to evaluate
18071          * @return {boolean} true if typeof oDD = DragDrop
18072          * @static
18073          */
18074         isTypeOfDD: function (oDD) {
18075             return (oDD && oDD.__ygDragDrop);
18076         },
18077
18078         /**
18079          * Utility function to determine if a given element has been
18080          * registered as a drag drop handle for the given Drag Drop object.
18081          * @method isHandle
18082          * @param {String} id the element id to check
18083          * @return {boolean} true if this element is a DragDrop handle, false
18084          * otherwise
18085          * @static
18086          */
18087         isHandle: function(sDDId, sHandleId) {
18088             return ( this.handleIds[sDDId] &&
18089                             this.handleIds[sDDId][sHandleId] );
18090         },
18091
18092         /**
18093          * Returns the DragDrop instance for a given id
18094          * @method getDDById
18095          * @param {String} id the id of the DragDrop object
18096          * @return {DragDrop} the drag drop object, null if it is not found
18097          * @static
18098          */
18099         getDDById: function(id) {
18100             for (var i in this.ids) {
18101                 if (this.ids[i][id]) {
18102                     return this.ids[i][id];
18103                 }
18104             }
18105             return null;
18106         },
18107
18108         /**
18109          * Fired after a registered DragDrop object gets the mousedown event.
18110          * Sets up the events required to track the object being dragged
18111          * @method handleMouseDown
18112          * @param {Event} e the event
18113          * @param oDD the DragDrop object being dragged
18114          * @private
18115          * @static
18116          */
18117         handleMouseDown: function(e, oDD) {
18118             if(Roo.QuickTips){
18119                 Roo.QuickTips.disable();
18120             }
18121             this.currentTarget = e.getTarget();
18122
18123             this.dragCurrent = oDD;
18124
18125             var el = oDD.getEl();
18126
18127             // track start position
18128             this.startX = e.getPageX();
18129             this.startY = e.getPageY();
18130
18131             this.deltaX = this.startX - el.offsetLeft;
18132             this.deltaY = this.startY - el.offsetTop;
18133
18134             this.dragThreshMet = false;
18135
18136             this.clickTimeout = setTimeout(
18137                     function() {
18138                         var DDM = Roo.dd.DDM;
18139                         DDM.startDrag(DDM.startX, DDM.startY);
18140                     },
18141                     this.clickTimeThresh );
18142         },
18143
18144         /**
18145          * Fired when either the drag pixel threshol or the mousedown hold
18146          * time threshold has been met.
18147          * @method startDrag
18148          * @param x {int} the X position of the original mousedown
18149          * @param y {int} the Y position of the original mousedown
18150          * @static
18151          */
18152         startDrag: function(x, y) {
18153             clearTimeout(this.clickTimeout);
18154             if (this.dragCurrent) {
18155                 this.dragCurrent.b4StartDrag(x, y);
18156                 this.dragCurrent.startDrag(x, y);
18157             }
18158             this.dragThreshMet = true;
18159         },
18160
18161         /**
18162          * Internal function to handle the mouseup event.  Will be invoked
18163          * from the context of the document.
18164          * @method handleMouseUp
18165          * @param {Event} e the event
18166          * @private
18167          * @static
18168          */
18169         handleMouseUp: function(e) {
18170
18171             if(Roo.QuickTips){
18172                 Roo.QuickTips.enable();
18173             }
18174             if (! this.dragCurrent) {
18175                 return;
18176             }
18177
18178             clearTimeout(this.clickTimeout);
18179
18180             if (this.dragThreshMet) {
18181                 this.fireEvents(e, true);
18182             } else {
18183             }
18184
18185             this.stopDrag(e);
18186
18187             this.stopEvent(e);
18188         },
18189
18190         /**
18191          * Utility to stop event propagation and event default, if these
18192          * features are turned on.
18193          * @method stopEvent
18194          * @param {Event} e the event as returned by this.getEvent()
18195          * @static
18196          */
18197         stopEvent: function(e){
18198             if(this.stopPropagation) {
18199                 e.stopPropagation();
18200             }
18201
18202             if (this.preventDefault) {
18203                 e.preventDefault();
18204             }
18205         },
18206
18207         /**
18208          * Internal function to clean up event handlers after the drag
18209          * operation is complete
18210          * @method stopDrag
18211          * @param {Event} e the event
18212          * @private
18213          * @static
18214          */
18215         stopDrag: function(e) {
18216             // Fire the drag end event for the item that was dragged
18217             if (this.dragCurrent) {
18218                 if (this.dragThreshMet) {
18219                     this.dragCurrent.b4EndDrag(e);
18220                     this.dragCurrent.endDrag(e);
18221                 }
18222
18223                 this.dragCurrent.onMouseUp(e);
18224             }
18225
18226             this.dragCurrent = null;
18227             this.dragOvers = {};
18228         },
18229
18230         /**
18231          * Internal function to handle the mousemove event.  Will be invoked
18232          * from the context of the html element.
18233          *
18234          * @TODO figure out what we can do about mouse events lost when the
18235          * user drags objects beyond the window boundary.  Currently we can
18236          * detect this in internet explorer by verifying that the mouse is
18237          * down during the mousemove event.  Firefox doesn't give us the
18238          * button state on the mousemove event.
18239          * @method handleMouseMove
18240          * @param {Event} e the event
18241          * @private
18242          * @static
18243          */
18244         handleMouseMove: function(e) {
18245             if (! this.dragCurrent) {
18246                 return true;
18247             }
18248
18249             // var button = e.which || e.button;
18250
18251             // check for IE mouseup outside of page boundary
18252             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18253                 this.stopEvent(e);
18254                 return this.handleMouseUp(e);
18255             }
18256
18257             if (!this.dragThreshMet) {
18258                 var diffX = Math.abs(this.startX - e.getPageX());
18259                 var diffY = Math.abs(this.startY - e.getPageY());
18260                 if (diffX > this.clickPixelThresh ||
18261                             diffY > this.clickPixelThresh) {
18262                     this.startDrag(this.startX, this.startY);
18263                 }
18264             }
18265
18266             if (this.dragThreshMet) {
18267                 this.dragCurrent.b4Drag(e);
18268                 this.dragCurrent.onDrag(e);
18269                 if(!this.dragCurrent.moveOnly){
18270                     this.fireEvents(e, false);
18271                 }
18272             }
18273
18274             this.stopEvent(e);
18275
18276             return true;
18277         },
18278
18279         /**
18280          * Iterates over all of the DragDrop elements to find ones we are
18281          * hovering over or dropping on
18282          * @method fireEvents
18283          * @param {Event} e the event
18284          * @param {boolean} isDrop is this a drop op or a mouseover op?
18285          * @private
18286          * @static
18287          */
18288         fireEvents: function(e, isDrop) {
18289             var dc = this.dragCurrent;
18290
18291             // If the user did the mouse up outside of the window, we could
18292             // get here even though we have ended the drag.
18293             if (!dc || dc.isLocked()) {
18294                 return;
18295             }
18296
18297             var pt = e.getPoint();
18298
18299             // cache the previous dragOver array
18300             var oldOvers = [];
18301
18302             var outEvts   = [];
18303             var overEvts  = [];
18304             var dropEvts  = [];
18305             var enterEvts = [];
18306
18307             // Check to see if the object(s) we were hovering over is no longer
18308             // being hovered over so we can fire the onDragOut event
18309             for (var i in this.dragOvers) {
18310
18311                 var ddo = this.dragOvers[i];
18312
18313                 if (! this.isTypeOfDD(ddo)) {
18314                     continue;
18315                 }
18316
18317                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18318                     outEvts.push( ddo );
18319                 }
18320
18321                 oldOvers[i] = true;
18322                 delete this.dragOvers[i];
18323             }
18324
18325             for (var sGroup in dc.groups) {
18326
18327                 if ("string" != typeof sGroup) {
18328                     continue;
18329                 }
18330
18331                 for (i in this.ids[sGroup]) {
18332                     var oDD = this.ids[sGroup][i];
18333                     if (! this.isTypeOfDD(oDD)) {
18334                         continue;
18335                     }
18336
18337                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18338                         if (this.isOverTarget(pt, oDD, this.mode)) {
18339                             // look for drop interactions
18340                             if (isDrop) {
18341                                 dropEvts.push( oDD );
18342                             // look for drag enter and drag over interactions
18343                             } else {
18344
18345                                 // initial drag over: dragEnter fires
18346                                 if (!oldOvers[oDD.id]) {
18347                                     enterEvts.push( oDD );
18348                                 // subsequent drag overs: dragOver fires
18349                                 } else {
18350                                     overEvts.push( oDD );
18351                                 }
18352
18353                                 this.dragOvers[oDD.id] = oDD;
18354                             }
18355                         }
18356                     }
18357                 }
18358             }
18359
18360             if (this.mode) {
18361                 if (outEvts.length) {
18362                     dc.b4DragOut(e, outEvts);
18363                     dc.onDragOut(e, outEvts);
18364                 }
18365
18366                 if (enterEvts.length) {
18367                     dc.onDragEnter(e, enterEvts);
18368                 }
18369
18370                 if (overEvts.length) {
18371                     dc.b4DragOver(e, overEvts);
18372                     dc.onDragOver(e, overEvts);
18373                 }
18374
18375                 if (dropEvts.length) {
18376                     dc.b4DragDrop(e, dropEvts);
18377                     dc.onDragDrop(e, dropEvts);
18378                 }
18379
18380             } else {
18381                 // fire dragout events
18382                 var len = 0;
18383                 for (i=0, len=outEvts.length; i<len; ++i) {
18384                     dc.b4DragOut(e, outEvts[i].id);
18385                     dc.onDragOut(e, outEvts[i].id);
18386                 }
18387
18388                 // fire enter events
18389                 for (i=0,len=enterEvts.length; i<len; ++i) {
18390                     // dc.b4DragEnter(e, oDD.id);
18391                     dc.onDragEnter(e, enterEvts[i].id);
18392                 }
18393
18394                 // fire over events
18395                 for (i=0,len=overEvts.length; i<len; ++i) {
18396                     dc.b4DragOver(e, overEvts[i].id);
18397                     dc.onDragOver(e, overEvts[i].id);
18398                 }
18399
18400                 // fire drop events
18401                 for (i=0, len=dropEvts.length; i<len; ++i) {
18402                     dc.b4DragDrop(e, dropEvts[i].id);
18403                     dc.onDragDrop(e, dropEvts[i].id);
18404                 }
18405
18406             }
18407
18408             // notify about a drop that did not find a target
18409             if (isDrop && !dropEvts.length) {
18410                 dc.onInvalidDrop(e);
18411             }
18412
18413         },
18414
18415         /**
18416          * Helper function for getting the best match from the list of drag
18417          * and drop objects returned by the drag and drop events when we are
18418          * in INTERSECT mode.  It returns either the first object that the
18419          * cursor is over, or the object that has the greatest overlap with
18420          * the dragged element.
18421          * @method getBestMatch
18422          * @param  {DragDrop[]} dds The array of drag and drop objects
18423          * targeted
18424          * @return {DragDrop}       The best single match
18425          * @static
18426          */
18427         getBestMatch: function(dds) {
18428             var winner = null;
18429             // Return null if the input is not what we expect
18430             //if (!dds || !dds.length || dds.length == 0) {
18431                // winner = null;
18432             // If there is only one item, it wins
18433             //} else if (dds.length == 1) {
18434
18435             var len = dds.length;
18436
18437             if (len == 1) {
18438                 winner = dds[0];
18439             } else {
18440                 // Loop through the targeted items
18441                 for (var i=0; i<len; ++i) {
18442                     var dd = dds[i];
18443                     // If the cursor is over the object, it wins.  If the
18444                     // cursor is over multiple matches, the first one we come
18445                     // to wins.
18446                     if (dd.cursorIsOver) {
18447                         winner = dd;
18448                         break;
18449                     // Otherwise the object with the most overlap wins
18450                     } else {
18451                         if (!winner ||
18452                             winner.overlap.getArea() < dd.overlap.getArea()) {
18453                             winner = dd;
18454                         }
18455                     }
18456                 }
18457             }
18458
18459             return winner;
18460         },
18461
18462         /**
18463          * Refreshes the cache of the top-left and bottom-right points of the
18464          * drag and drop objects in the specified group(s).  This is in the
18465          * format that is stored in the drag and drop instance, so typical
18466          * usage is:
18467          * <code>
18468          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18469          * </code>
18470          * Alternatively:
18471          * <code>
18472          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18473          * </code>
18474          * @TODO this really should be an indexed array.  Alternatively this
18475          * method could accept both.
18476          * @method refreshCache
18477          * @param {Object} groups an associative array of groups to refresh
18478          * @static
18479          */
18480         refreshCache: function(groups) {
18481             for (var sGroup in groups) {
18482                 if ("string" != typeof sGroup) {
18483                     continue;
18484                 }
18485                 for (var i in this.ids[sGroup]) {
18486                     var oDD = this.ids[sGroup][i];
18487
18488                     if (this.isTypeOfDD(oDD)) {
18489                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18490                         var loc = this.getLocation(oDD);
18491                         if (loc) {
18492                             this.locationCache[oDD.id] = loc;
18493                         } else {
18494                             delete this.locationCache[oDD.id];
18495                             // this will unregister the drag and drop object if
18496                             // the element is not in a usable state
18497                             // oDD.unreg();
18498                         }
18499                     }
18500                 }
18501             }
18502         },
18503
18504         /**
18505          * This checks to make sure an element exists and is in the DOM.  The
18506          * main purpose is to handle cases where innerHTML is used to remove
18507          * drag and drop objects from the DOM.  IE provides an 'unspecified
18508          * error' when trying to access the offsetParent of such an element
18509          * @method verifyEl
18510          * @param {HTMLElement} el the element to check
18511          * @return {boolean} true if the element looks usable
18512          * @static
18513          */
18514         verifyEl: function(el) {
18515             if (el) {
18516                 var parent;
18517                 if(Roo.isIE){
18518                     try{
18519                         parent = el.offsetParent;
18520                     }catch(e){}
18521                 }else{
18522                     parent = el.offsetParent;
18523                 }
18524                 if (parent) {
18525                     return true;
18526                 }
18527             }
18528
18529             return false;
18530         },
18531
18532         /**
18533          * Returns a Region object containing the drag and drop element's position
18534          * and size, including the padding configured for it
18535          * @method getLocation
18536          * @param {DragDrop} oDD the drag and drop object to get the
18537          *                       location for
18538          * @return {Roo.lib.Region} a Region object representing the total area
18539          *                             the element occupies, including any padding
18540          *                             the instance is configured for.
18541          * @static
18542          */
18543         getLocation: function(oDD) {
18544             if (! this.isTypeOfDD(oDD)) {
18545                 return null;
18546             }
18547
18548             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18549
18550             try {
18551                 pos= Roo.lib.Dom.getXY(el);
18552             } catch (e) { }
18553
18554             if (!pos) {
18555                 return null;
18556             }
18557
18558             x1 = pos[0];
18559             x2 = x1 + el.offsetWidth;
18560             y1 = pos[1];
18561             y2 = y1 + el.offsetHeight;
18562
18563             t = y1 - oDD.padding[0];
18564             r = x2 + oDD.padding[1];
18565             b = y2 + oDD.padding[2];
18566             l = x1 - oDD.padding[3];
18567
18568             return new Roo.lib.Region( t, r, b, l );
18569         },
18570
18571         /**
18572          * Checks the cursor location to see if it over the target
18573          * @method isOverTarget
18574          * @param {Roo.lib.Point} pt The point to evaluate
18575          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18576          * @return {boolean} true if the mouse is over the target
18577          * @private
18578          * @static
18579          */
18580         isOverTarget: function(pt, oTarget, intersect) {
18581             // use cache if available
18582             var loc = this.locationCache[oTarget.id];
18583             if (!loc || !this.useCache) {
18584                 loc = this.getLocation(oTarget);
18585                 this.locationCache[oTarget.id] = loc;
18586
18587             }
18588
18589             if (!loc) {
18590                 return false;
18591             }
18592
18593             oTarget.cursorIsOver = loc.contains( pt );
18594
18595             // DragDrop is using this as a sanity check for the initial mousedown
18596             // in this case we are done.  In POINT mode, if the drag obj has no
18597             // contraints, we are also done. Otherwise we need to evaluate the
18598             // location of the target as related to the actual location of the
18599             // dragged element.
18600             var dc = this.dragCurrent;
18601             if (!dc || !dc.getTargetCoord ||
18602                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18603                 return oTarget.cursorIsOver;
18604             }
18605
18606             oTarget.overlap = null;
18607
18608             // Get the current location of the drag element, this is the
18609             // location of the mouse event less the delta that represents
18610             // where the original mousedown happened on the element.  We
18611             // need to consider constraints and ticks as well.
18612             var pos = dc.getTargetCoord(pt.x, pt.y);
18613
18614             var el = dc.getDragEl();
18615             var curRegion = new Roo.lib.Region( pos.y,
18616                                                    pos.x + el.offsetWidth,
18617                                                    pos.y + el.offsetHeight,
18618                                                    pos.x );
18619
18620             var overlap = curRegion.intersect(loc);
18621
18622             if (overlap) {
18623                 oTarget.overlap = overlap;
18624                 return (intersect) ? true : oTarget.cursorIsOver;
18625             } else {
18626                 return false;
18627             }
18628         },
18629
18630         /**
18631          * unload event handler
18632          * @method _onUnload
18633          * @private
18634          * @static
18635          */
18636         _onUnload: function(e, me) {
18637             Roo.dd.DragDropMgr.unregAll();
18638         },
18639
18640         /**
18641          * Cleans up the drag and drop events and objects.
18642          * @method unregAll
18643          * @private
18644          * @static
18645          */
18646         unregAll: function() {
18647
18648             if (this.dragCurrent) {
18649                 this.stopDrag();
18650                 this.dragCurrent = null;
18651             }
18652
18653             this._execOnAll("unreg", []);
18654
18655             for (i in this.elementCache) {
18656                 delete this.elementCache[i];
18657             }
18658
18659             this.elementCache = {};
18660             this.ids = {};
18661         },
18662
18663         /**
18664          * A cache of DOM elements
18665          * @property elementCache
18666          * @private
18667          * @static
18668          */
18669         elementCache: {},
18670
18671         /**
18672          * Get the wrapper for the DOM element specified
18673          * @method getElWrapper
18674          * @param {String} id the id of the element to get
18675          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18676          * @private
18677          * @deprecated This wrapper isn't that useful
18678          * @static
18679          */
18680         getElWrapper: function(id) {
18681             var oWrapper = this.elementCache[id];
18682             if (!oWrapper || !oWrapper.el) {
18683                 oWrapper = this.elementCache[id] =
18684                     new this.ElementWrapper(Roo.getDom(id));
18685             }
18686             return oWrapper;
18687         },
18688
18689         /**
18690          * Returns the actual DOM element
18691          * @method getElement
18692          * @param {String} id the id of the elment to get
18693          * @return {Object} The element
18694          * @deprecated use Roo.getDom instead
18695          * @static
18696          */
18697         getElement: function(id) {
18698             return Roo.getDom(id);
18699         },
18700
18701         /**
18702          * Returns the style property for the DOM element (i.e.,
18703          * document.getElById(id).style)
18704          * @method getCss
18705          * @param {String} id the id of the elment to get
18706          * @return {Object} The style property of the element
18707          * @deprecated use Roo.getDom instead
18708          * @static
18709          */
18710         getCss: function(id) {
18711             var el = Roo.getDom(id);
18712             return (el) ? el.style : null;
18713         },
18714
18715         /**
18716          * Inner class for cached elements
18717          * @class DragDropMgr.ElementWrapper
18718          * @for DragDropMgr
18719          * @private
18720          * @deprecated
18721          */
18722         ElementWrapper: function(el) {
18723                 /**
18724                  * The element
18725                  * @property el
18726                  */
18727                 this.el = el || null;
18728                 /**
18729                  * The element id
18730                  * @property id
18731                  */
18732                 this.id = this.el && el.id;
18733                 /**
18734                  * A reference to the style property
18735                  * @property css
18736                  */
18737                 this.css = this.el && el.style;
18738             },
18739
18740         /**
18741          * Returns the X position of an html element
18742          * @method getPosX
18743          * @param el the element for which to get the position
18744          * @return {int} the X coordinate
18745          * @for DragDropMgr
18746          * @deprecated use Roo.lib.Dom.getX instead
18747          * @static
18748          */
18749         getPosX: function(el) {
18750             return Roo.lib.Dom.getX(el);
18751         },
18752
18753         /**
18754          * Returns the Y position of an html element
18755          * @method getPosY
18756          * @param el the element for which to get the position
18757          * @return {int} the Y coordinate
18758          * @deprecated use Roo.lib.Dom.getY instead
18759          * @static
18760          */
18761         getPosY: function(el) {
18762             return Roo.lib.Dom.getY(el);
18763         },
18764
18765         /**
18766          * Swap two nodes.  In IE, we use the native method, for others we
18767          * emulate the IE behavior
18768          * @method swapNode
18769          * @param n1 the first node to swap
18770          * @param n2 the other node to swap
18771          * @static
18772          */
18773         swapNode: function(n1, n2) {
18774             if (n1.swapNode) {
18775                 n1.swapNode(n2);
18776             } else {
18777                 var p = n2.parentNode;
18778                 var s = n2.nextSibling;
18779
18780                 if (s == n1) {
18781                     p.insertBefore(n1, n2);
18782                 } else if (n2 == n1.nextSibling) {
18783                     p.insertBefore(n2, n1);
18784                 } else {
18785                     n1.parentNode.replaceChild(n2, n1);
18786                     p.insertBefore(n1, s);
18787                 }
18788             }
18789         },
18790
18791         /**
18792          * Returns the current scroll position
18793          * @method getScroll
18794          * @private
18795          * @static
18796          */
18797         getScroll: function () {
18798             var t, l, dde=document.documentElement, db=document.body;
18799             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18800                 t = dde.scrollTop;
18801                 l = dde.scrollLeft;
18802             } else if (db) {
18803                 t = db.scrollTop;
18804                 l = db.scrollLeft;
18805             } else {
18806
18807             }
18808             return { top: t, left: l };
18809         },
18810
18811         /**
18812          * Returns the specified element style property
18813          * @method getStyle
18814          * @param {HTMLElement} el          the element
18815          * @param {string}      styleProp   the style property
18816          * @return {string} The value of the style property
18817          * @deprecated use Roo.lib.Dom.getStyle
18818          * @static
18819          */
18820         getStyle: function(el, styleProp) {
18821             return Roo.fly(el).getStyle(styleProp);
18822         },
18823
18824         /**
18825          * Gets the scrollTop
18826          * @method getScrollTop
18827          * @return {int} the document's scrollTop
18828          * @static
18829          */
18830         getScrollTop: function () { return this.getScroll().top; },
18831
18832         /**
18833          * Gets the scrollLeft
18834          * @method getScrollLeft
18835          * @return {int} the document's scrollTop
18836          * @static
18837          */
18838         getScrollLeft: function () { return this.getScroll().left; },
18839
18840         /**
18841          * Sets the x/y position of an element to the location of the
18842          * target element.
18843          * @method moveToEl
18844          * @param {HTMLElement} moveEl      The element to move
18845          * @param {HTMLElement} targetEl    The position reference element
18846          * @static
18847          */
18848         moveToEl: function (moveEl, targetEl) {
18849             var aCoord = Roo.lib.Dom.getXY(targetEl);
18850             Roo.lib.Dom.setXY(moveEl, aCoord);
18851         },
18852
18853         /**
18854          * Numeric array sort function
18855          * @method numericSort
18856          * @static
18857          */
18858         numericSort: function(a, b) { return (a - b); },
18859
18860         /**
18861          * Internal counter
18862          * @property _timeoutCount
18863          * @private
18864          * @static
18865          */
18866         _timeoutCount: 0,
18867
18868         /**
18869          * Trying to make the load order less important.  Without this we get
18870          * an error if this file is loaded before the Event Utility.
18871          * @method _addListeners
18872          * @private
18873          * @static
18874          */
18875         _addListeners: function() {
18876             var DDM = Roo.dd.DDM;
18877             if ( Roo.lib.Event && document ) {
18878                 DDM._onLoad();
18879             } else {
18880                 if (DDM._timeoutCount > 2000) {
18881                 } else {
18882                     setTimeout(DDM._addListeners, 10);
18883                     if (document && document.body) {
18884                         DDM._timeoutCount += 1;
18885                     }
18886                 }
18887             }
18888         },
18889
18890         /**
18891          * Recursively searches the immediate parent and all child nodes for
18892          * the handle element in order to determine wheter or not it was
18893          * clicked.
18894          * @method handleWasClicked
18895          * @param node the html element to inspect
18896          * @static
18897          */
18898         handleWasClicked: function(node, id) {
18899             if (this.isHandle(id, node.id)) {
18900                 return true;
18901             } else {
18902                 // check to see if this is a text node child of the one we want
18903                 var p = node.parentNode;
18904
18905                 while (p) {
18906                     if (this.isHandle(id, p.id)) {
18907                         return true;
18908                     } else {
18909                         p = p.parentNode;
18910                     }
18911                 }
18912             }
18913
18914             return false;
18915         }
18916
18917     };
18918
18919 }();
18920
18921 // shorter alias, save a few bytes
18922 Roo.dd.DDM = Roo.dd.DragDropMgr;
18923 Roo.dd.DDM._addListeners();
18924
18925 }/*
18926  * Based on:
18927  * Ext JS Library 1.1.1
18928  * Copyright(c) 2006-2007, Ext JS, LLC.
18929  *
18930  * Originally Released Under LGPL - original licence link has changed is not relivant.
18931  *
18932  * Fork - LGPL
18933  * <script type="text/javascript">
18934  */
18935
18936 /**
18937  * @class Roo.dd.DD
18938  * A DragDrop implementation where the linked element follows the
18939  * mouse cursor during a drag.
18940  * @extends Roo.dd.DragDrop
18941  * @constructor
18942  * @param {String} id the id of the linked element
18943  * @param {String} sGroup the group of related DragDrop items
18944  * @param {object} config an object containing configurable attributes
18945  *                Valid properties for DD:
18946  *                    scroll
18947  */
18948 Roo.dd.DD = function(id, sGroup, config) {
18949     if (id) {
18950         this.init(id, sGroup, config);
18951     }
18952 };
18953
18954 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18955
18956     /**
18957      * When set to true, the utility automatically tries to scroll the browser
18958      * window wehn a drag and drop element is dragged near the viewport boundary.
18959      * Defaults to true.
18960      * @property scroll
18961      * @type boolean
18962      */
18963     scroll: true,
18964
18965     /**
18966      * Sets the pointer offset to the distance between the linked element's top
18967      * left corner and the location the element was clicked
18968      * @method autoOffset
18969      * @param {int} iPageX the X coordinate of the click
18970      * @param {int} iPageY the Y coordinate of the click
18971      */
18972     autoOffset: function(iPageX, iPageY) {
18973         var x = iPageX - this.startPageX;
18974         var y = iPageY - this.startPageY;
18975         this.setDelta(x, y);
18976     },
18977
18978     /**
18979      * Sets the pointer offset.  You can call this directly to force the
18980      * offset to be in a particular location (e.g., pass in 0,0 to set it
18981      * to the center of the object)
18982      * @method setDelta
18983      * @param {int} iDeltaX the distance from the left
18984      * @param {int} iDeltaY the distance from the top
18985      */
18986     setDelta: function(iDeltaX, iDeltaY) {
18987         this.deltaX = iDeltaX;
18988         this.deltaY = iDeltaY;
18989     },
18990
18991     /**
18992      * Sets the drag element to the location of the mousedown or click event,
18993      * maintaining the cursor location relative to the location on the element
18994      * that was clicked.  Override this if you want to place the element in a
18995      * location other than where the cursor is.
18996      * @method setDragElPos
18997      * @param {int} iPageX the X coordinate of the mousedown or drag event
18998      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18999      */
19000     setDragElPos: function(iPageX, iPageY) {
19001         // the first time we do this, we are going to check to make sure
19002         // the element has css positioning
19003
19004         var el = this.getDragEl();
19005         this.alignElWithMouse(el, iPageX, iPageY);
19006     },
19007
19008     /**
19009      * Sets the element to the location of the mousedown or click event,
19010      * maintaining the cursor location relative to the location on the element
19011      * that was clicked.  Override this if you want to place the element in a
19012      * location other than where the cursor is.
19013      * @method alignElWithMouse
19014      * @param {HTMLElement} el the element to move
19015      * @param {int} iPageX the X coordinate of the mousedown or drag event
19016      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19017      */
19018     alignElWithMouse: function(el, iPageX, iPageY) {
19019         var oCoord = this.getTargetCoord(iPageX, iPageY);
19020         var fly = el.dom ? el : Roo.fly(el);
19021         if (!this.deltaSetXY) {
19022             var aCoord = [oCoord.x, oCoord.y];
19023             fly.setXY(aCoord);
19024             var newLeft = fly.getLeft(true);
19025             var newTop  = fly.getTop(true);
19026             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19027         } else {
19028             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19029         }
19030
19031         this.cachePosition(oCoord.x, oCoord.y);
19032         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19033         return oCoord;
19034     },
19035
19036     /**
19037      * Saves the most recent position so that we can reset the constraints and
19038      * tick marks on-demand.  We need to know this so that we can calculate the
19039      * number of pixels the element is offset from its original position.
19040      * @method cachePosition
19041      * @param iPageX the current x position (optional, this just makes it so we
19042      * don't have to look it up again)
19043      * @param iPageY the current y position (optional, this just makes it so we
19044      * don't have to look it up again)
19045      */
19046     cachePosition: function(iPageX, iPageY) {
19047         if (iPageX) {
19048             this.lastPageX = iPageX;
19049             this.lastPageY = iPageY;
19050         } else {
19051             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19052             this.lastPageX = aCoord[0];
19053             this.lastPageY = aCoord[1];
19054         }
19055     },
19056
19057     /**
19058      * Auto-scroll the window if the dragged object has been moved beyond the
19059      * visible window boundary.
19060      * @method autoScroll
19061      * @param {int} x the drag element's x position
19062      * @param {int} y the drag element's y position
19063      * @param {int} h the height of the drag element
19064      * @param {int} w the width of the drag element
19065      * @private
19066      */
19067     autoScroll: function(x, y, h, w) {
19068
19069         if (this.scroll) {
19070             // The client height
19071             var clientH = Roo.lib.Dom.getViewWidth();
19072
19073             // The client width
19074             var clientW = Roo.lib.Dom.getViewHeight();
19075
19076             // The amt scrolled down
19077             var st = this.DDM.getScrollTop();
19078
19079             // The amt scrolled right
19080             var sl = this.DDM.getScrollLeft();
19081
19082             // Location of the bottom of the element
19083             var bot = h + y;
19084
19085             // Location of the right of the element
19086             var right = w + x;
19087
19088             // The distance from the cursor to the bottom of the visible area,
19089             // adjusted so that we don't scroll if the cursor is beyond the
19090             // element drag constraints
19091             var toBot = (clientH + st - y - this.deltaY);
19092
19093             // The distance from the cursor to the right of the visible area
19094             var toRight = (clientW + sl - x - this.deltaX);
19095
19096
19097             // How close to the edge the cursor must be before we scroll
19098             // var thresh = (document.all) ? 100 : 40;
19099             var thresh = 40;
19100
19101             // How many pixels to scroll per autoscroll op.  This helps to reduce
19102             // clunky scrolling. IE is more sensitive about this ... it needs this
19103             // value to be higher.
19104             var scrAmt = (document.all) ? 80 : 30;
19105
19106             // Scroll down if we are near the bottom of the visible page and the
19107             // obj extends below the crease
19108             if ( bot > clientH && toBot < thresh ) {
19109                 window.scrollTo(sl, st + scrAmt);
19110             }
19111
19112             // Scroll up if the window is scrolled down and the top of the object
19113             // goes above the top border
19114             if ( y < st && st > 0 && y - st < thresh ) {
19115                 window.scrollTo(sl, st - scrAmt);
19116             }
19117
19118             // Scroll right if the obj is beyond the right border and the cursor is
19119             // near the border.
19120             if ( right > clientW && toRight < thresh ) {
19121                 window.scrollTo(sl + scrAmt, st);
19122             }
19123
19124             // Scroll left if the window has been scrolled to the right and the obj
19125             // extends past the left border
19126             if ( x < sl && sl > 0 && x - sl < thresh ) {
19127                 window.scrollTo(sl - scrAmt, st);
19128             }
19129         }
19130     },
19131
19132     /**
19133      * Finds the location the element should be placed if we want to move
19134      * it to where the mouse location less the click offset would place us.
19135      * @method getTargetCoord
19136      * @param {int} iPageX the X coordinate of the click
19137      * @param {int} iPageY the Y coordinate of the click
19138      * @return an object that contains the coordinates (Object.x and Object.y)
19139      * @private
19140      */
19141     getTargetCoord: function(iPageX, iPageY) {
19142
19143
19144         var x = iPageX - this.deltaX;
19145         var y = iPageY - this.deltaY;
19146
19147         if (this.constrainX) {
19148             if (x < this.minX) { x = this.minX; }
19149             if (x > this.maxX) { x = this.maxX; }
19150         }
19151
19152         if (this.constrainY) {
19153             if (y < this.minY) { y = this.minY; }
19154             if (y > this.maxY) { y = this.maxY; }
19155         }
19156
19157         x = this.getTick(x, this.xTicks);
19158         y = this.getTick(y, this.yTicks);
19159
19160
19161         return {x:x, y:y};
19162     },
19163
19164     /*
19165      * Sets up config options specific to this class. Overrides
19166      * Roo.dd.DragDrop, but all versions of this method through the
19167      * inheritance chain are called
19168      */
19169     applyConfig: function() {
19170         Roo.dd.DD.superclass.applyConfig.call(this);
19171         this.scroll = (this.config.scroll !== false);
19172     },
19173
19174     /*
19175      * Event that fires prior to the onMouseDown event.  Overrides
19176      * Roo.dd.DragDrop.
19177      */
19178     b4MouseDown: function(e) {
19179         // this.resetConstraints();
19180         this.autoOffset(e.getPageX(),
19181                             e.getPageY());
19182     },
19183
19184     /*
19185      * Event that fires prior to the onDrag event.  Overrides
19186      * Roo.dd.DragDrop.
19187      */
19188     b4Drag: function(e) {
19189         this.setDragElPos(e.getPageX(),
19190                             e.getPageY());
19191     },
19192
19193     toString: function() {
19194         return ("DD " + this.id);
19195     }
19196
19197     //////////////////////////////////////////////////////////////////////////
19198     // Debugging ygDragDrop events that can be overridden
19199     //////////////////////////////////////////////////////////////////////////
19200     /*
19201     startDrag: function(x, y) {
19202     },
19203
19204     onDrag: function(e) {
19205     },
19206
19207     onDragEnter: function(e, id) {
19208     },
19209
19210     onDragOver: function(e, id) {
19211     },
19212
19213     onDragOut: function(e, id) {
19214     },
19215
19216     onDragDrop: function(e, id) {
19217     },
19218
19219     endDrag: function(e) {
19220     }
19221
19222     */
19223
19224 });/*
19225  * Based on:
19226  * Ext JS Library 1.1.1
19227  * Copyright(c) 2006-2007, Ext JS, LLC.
19228  *
19229  * Originally Released Under LGPL - original licence link has changed is not relivant.
19230  *
19231  * Fork - LGPL
19232  * <script type="text/javascript">
19233  */
19234
19235 /**
19236  * @class Roo.dd.DDProxy
19237  * A DragDrop implementation that inserts an empty, bordered div into
19238  * the document that follows the cursor during drag operations.  At the time of
19239  * the click, the frame div is resized to the dimensions of the linked html
19240  * element, and moved to the exact location of the linked element.
19241  *
19242  * References to the "frame" element refer to the single proxy element that
19243  * was created to be dragged in place of all DDProxy elements on the
19244  * page.
19245  *
19246  * @extends Roo.dd.DD
19247  * @constructor
19248  * @param {String} id the id of the linked html element
19249  * @param {String} sGroup the group of related DragDrop objects
19250  * @param {object} config an object containing configurable attributes
19251  *                Valid properties for DDProxy in addition to those in DragDrop:
19252  *                   resizeFrame, centerFrame, dragElId
19253  */
19254 Roo.dd.DDProxy = function(id, sGroup, config) {
19255     if (id) {
19256         this.init(id, sGroup, config);
19257         this.initFrame();
19258     }
19259 };
19260
19261 /**
19262  * The default drag frame div id
19263  * @property Roo.dd.DDProxy.dragElId
19264  * @type String
19265  * @static
19266  */
19267 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19268
19269 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19270
19271     /**
19272      * By default we resize the drag frame to be the same size as the element
19273      * we want to drag (this is to get the frame effect).  We can turn it off
19274      * if we want a different behavior.
19275      * @property resizeFrame
19276      * @type boolean
19277      */
19278     resizeFrame: true,
19279
19280     /**
19281      * By default the frame is positioned exactly where the drag element is, so
19282      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19283      * you do not have constraints on the obj is to have the drag frame centered
19284      * around the cursor.  Set centerFrame to true for this effect.
19285      * @property centerFrame
19286      * @type boolean
19287      */
19288     centerFrame: false,
19289
19290     /**
19291      * Creates the proxy element if it does not yet exist
19292      * @method createFrame
19293      */
19294     createFrame: function() {
19295         var self = this;
19296         var body = document.body;
19297
19298         if (!body || !body.firstChild) {
19299             setTimeout( function() { self.createFrame(); }, 50 );
19300             return;
19301         }
19302
19303         var div = this.getDragEl();
19304
19305         if (!div) {
19306             div    = document.createElement("div");
19307             div.id = this.dragElId;
19308             var s  = div.style;
19309
19310             s.position   = "absolute";
19311             s.visibility = "hidden";
19312             s.cursor     = "move";
19313             s.border     = "2px solid #aaa";
19314             s.zIndex     = 999;
19315
19316             // appendChild can blow up IE if invoked prior to the window load event
19317             // while rendering a table.  It is possible there are other scenarios
19318             // that would cause this to happen as well.
19319             body.insertBefore(div, body.firstChild);
19320         }
19321     },
19322
19323     /**
19324      * Initialization for the drag frame element.  Must be called in the
19325      * constructor of all subclasses
19326      * @method initFrame
19327      */
19328     initFrame: function() {
19329         this.createFrame();
19330     },
19331
19332     applyConfig: function() {
19333         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19334
19335         this.resizeFrame = (this.config.resizeFrame !== false);
19336         this.centerFrame = (this.config.centerFrame);
19337         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19338     },
19339
19340     /**
19341      * Resizes the drag frame to the dimensions of the clicked object, positions
19342      * it over the object, and finally displays it
19343      * @method showFrame
19344      * @param {int} iPageX X click position
19345      * @param {int} iPageY Y click position
19346      * @private
19347      */
19348     showFrame: function(iPageX, iPageY) {
19349         var el = this.getEl();
19350         var dragEl = this.getDragEl();
19351         var s = dragEl.style;
19352
19353         this._resizeProxy();
19354
19355         if (this.centerFrame) {
19356             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19357                            Math.round(parseInt(s.height, 10)/2) );
19358         }
19359
19360         this.setDragElPos(iPageX, iPageY);
19361
19362         Roo.fly(dragEl).show();
19363     },
19364
19365     /**
19366      * The proxy is automatically resized to the dimensions of the linked
19367      * element when a drag is initiated, unless resizeFrame is set to false
19368      * @method _resizeProxy
19369      * @private
19370      */
19371     _resizeProxy: function() {
19372         if (this.resizeFrame) {
19373             var el = this.getEl();
19374             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19375         }
19376     },
19377
19378     // overrides Roo.dd.DragDrop
19379     b4MouseDown: function(e) {
19380         var x = e.getPageX();
19381         var y = e.getPageY();
19382         this.autoOffset(x, y);
19383         this.setDragElPos(x, y);
19384     },
19385
19386     // overrides Roo.dd.DragDrop
19387     b4StartDrag: function(x, y) {
19388         // show the drag frame
19389         this.showFrame(x, y);
19390     },
19391
19392     // overrides Roo.dd.DragDrop
19393     b4EndDrag: function(e) {
19394         Roo.fly(this.getDragEl()).hide();
19395     },
19396
19397     // overrides Roo.dd.DragDrop
19398     // By default we try to move the element to the last location of the frame.
19399     // This is so that the default behavior mirrors that of Roo.dd.DD.
19400     endDrag: function(e) {
19401
19402         var lel = this.getEl();
19403         var del = this.getDragEl();
19404
19405         // Show the drag frame briefly so we can get its position
19406         del.style.visibility = "";
19407
19408         this.beforeMove();
19409         // Hide the linked element before the move to get around a Safari
19410         // rendering bug.
19411         lel.style.visibility = "hidden";
19412         Roo.dd.DDM.moveToEl(lel, del);
19413         del.style.visibility = "hidden";
19414         lel.style.visibility = "";
19415
19416         this.afterDrag();
19417     },
19418
19419     beforeMove : function(){
19420
19421     },
19422
19423     afterDrag : function(){
19424
19425     },
19426
19427     toString: function() {
19428         return ("DDProxy " + this.id);
19429     }
19430
19431 });
19432 /*
19433  * Based on:
19434  * Ext JS Library 1.1.1
19435  * Copyright(c) 2006-2007, Ext JS, LLC.
19436  *
19437  * Originally Released Under LGPL - original licence link has changed is not relivant.
19438  *
19439  * Fork - LGPL
19440  * <script type="text/javascript">
19441  */
19442
19443  /**
19444  * @class Roo.dd.DDTarget
19445  * A DragDrop implementation that does not move, but can be a drop
19446  * target.  You would get the same result by simply omitting implementation
19447  * for the event callbacks, but this way we reduce the processing cost of the
19448  * event listener and the callbacks.
19449  * @extends Roo.dd.DragDrop
19450  * @constructor
19451  * @param {String} id the id of the element that is a drop target
19452  * @param {String} sGroup the group of related DragDrop objects
19453  * @param {object} config an object containing configurable attributes
19454  *                 Valid properties for DDTarget in addition to those in
19455  *                 DragDrop:
19456  *                    none
19457  */
19458 Roo.dd.DDTarget = function(id, sGroup, config) {
19459     if (id) {
19460         this.initTarget(id, sGroup, config);
19461     }
19462     if (config.listeners || config.events) { 
19463        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19464             listeners : config.listeners || {}, 
19465             events : config.events || {} 
19466         });    
19467     }
19468 };
19469
19470 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19471 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19472     toString: function() {
19473         return ("DDTarget " + this.id);
19474     }
19475 });
19476 /*
19477  * Based on:
19478  * Ext JS Library 1.1.1
19479  * Copyright(c) 2006-2007, Ext JS, LLC.
19480  *
19481  * Originally Released Under LGPL - original licence link has changed is not relivant.
19482  *
19483  * Fork - LGPL
19484  * <script type="text/javascript">
19485  */
19486  
19487
19488 /**
19489  * @class Roo.dd.ScrollManager
19490  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19491  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19492  * @singleton
19493  */
19494 Roo.dd.ScrollManager = function(){
19495     var ddm = Roo.dd.DragDropMgr;
19496     var els = {};
19497     var dragEl = null;
19498     var proc = {};
19499     
19500     
19501     
19502     var onStop = function(e){
19503         dragEl = null;
19504         clearProc();
19505     };
19506     
19507     var triggerRefresh = function(){
19508         if(ddm.dragCurrent){
19509              ddm.refreshCache(ddm.dragCurrent.groups);
19510         }
19511     };
19512     
19513     var doScroll = function(){
19514         if(ddm.dragCurrent){
19515             var dds = Roo.dd.ScrollManager;
19516             if(!dds.animate){
19517                 if(proc.el.scroll(proc.dir, dds.increment)){
19518                     triggerRefresh();
19519                 }
19520             }else{
19521                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19522             }
19523         }
19524     };
19525     
19526     var clearProc = function(){
19527         if(proc.id){
19528             clearInterval(proc.id);
19529         }
19530         proc.id = 0;
19531         proc.el = null;
19532         proc.dir = "";
19533     };
19534     
19535     var startProc = function(el, dir){
19536          Roo.log('scroll startproc');
19537         clearProc();
19538         proc.el = el;
19539         proc.dir = dir;
19540         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19541     };
19542     
19543     var onFire = function(e, isDrop){
19544        
19545         if(isDrop || !ddm.dragCurrent){ return; }
19546         var dds = Roo.dd.ScrollManager;
19547         if(!dragEl || dragEl != ddm.dragCurrent){
19548             dragEl = ddm.dragCurrent;
19549             // refresh regions on drag start
19550             dds.refreshCache();
19551         }
19552         
19553         var xy = Roo.lib.Event.getXY(e);
19554         var pt = new Roo.lib.Point(xy[0], xy[1]);
19555         for(var id in els){
19556             var el = els[id], r = el._region;
19557             if(r && r.contains(pt) && el.isScrollable()){
19558                 if(r.bottom - pt.y <= dds.thresh){
19559                     if(proc.el != el){
19560                         startProc(el, "down");
19561                     }
19562                     return;
19563                 }else if(r.right - pt.x <= dds.thresh){
19564                     if(proc.el != el){
19565                         startProc(el, "left");
19566                     }
19567                     return;
19568                 }else if(pt.y - r.top <= dds.thresh){
19569                     if(proc.el != el){
19570                         startProc(el, "up");
19571                     }
19572                     return;
19573                 }else if(pt.x - r.left <= dds.thresh){
19574                     if(proc.el != el){
19575                         startProc(el, "right");
19576                     }
19577                     return;
19578                 }
19579             }
19580         }
19581         clearProc();
19582     };
19583     
19584     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19585     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19586     
19587     return {
19588         /**
19589          * Registers new overflow element(s) to auto scroll
19590          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19591          */
19592         register : function(el){
19593             if(el instanceof Array){
19594                 for(var i = 0, len = el.length; i < len; i++) {
19595                         this.register(el[i]);
19596                 }
19597             }else{
19598                 el = Roo.get(el);
19599                 els[el.id] = el;
19600             }
19601             Roo.dd.ScrollManager.els = els;
19602         },
19603         
19604         /**
19605          * Unregisters overflow element(s) so they are no longer scrolled
19606          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19607          */
19608         unregister : function(el){
19609             if(el instanceof Array){
19610                 for(var i = 0, len = el.length; i < len; i++) {
19611                         this.unregister(el[i]);
19612                 }
19613             }else{
19614                 el = Roo.get(el);
19615                 delete els[el.id];
19616             }
19617         },
19618         
19619         /**
19620          * The number of pixels from the edge of a container the pointer needs to be to 
19621          * trigger scrolling (defaults to 25)
19622          * @type Number
19623          */
19624         thresh : 25,
19625         
19626         /**
19627          * The number of pixels to scroll in each scroll increment (defaults to 50)
19628          * @type Number
19629          */
19630         increment : 100,
19631         
19632         /**
19633          * The frequency of scrolls in milliseconds (defaults to 500)
19634          * @type Number
19635          */
19636         frequency : 500,
19637         
19638         /**
19639          * True to animate the scroll (defaults to true)
19640          * @type Boolean
19641          */
19642         animate: true,
19643         
19644         /**
19645          * The animation duration in seconds - 
19646          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19647          * @type Number
19648          */
19649         animDuration: .4,
19650         
19651         /**
19652          * Manually trigger a cache refresh.
19653          */
19654         refreshCache : function(){
19655             for(var id in els){
19656                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19657                     els[id]._region = els[id].getRegion();
19658                 }
19659             }
19660         }
19661     };
19662 }();/*
19663  * Based on:
19664  * Ext JS Library 1.1.1
19665  * Copyright(c) 2006-2007, Ext JS, LLC.
19666  *
19667  * Originally Released Under LGPL - original licence link has changed is not relivant.
19668  *
19669  * Fork - LGPL
19670  * <script type="text/javascript">
19671  */
19672  
19673
19674 /**
19675  * @class Roo.dd.Registry
19676  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19677  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19678  * @singleton
19679  */
19680 Roo.dd.Registry = function(){
19681     var elements = {}; 
19682     var handles = {}; 
19683     var autoIdSeed = 0;
19684
19685     var getId = function(el, autogen){
19686         if(typeof el == "string"){
19687             return el;
19688         }
19689         var id = el.id;
19690         if(!id && autogen !== false){
19691             id = "roodd-" + (++autoIdSeed);
19692             el.id = id;
19693         }
19694         return id;
19695     };
19696     
19697     return {
19698     /**
19699      * Register a drag drop element
19700      * @param {String|HTMLElement} element The id or DOM node to register
19701      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19702      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19703      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19704      * populated in the data object (if applicable):
19705      * <pre>
19706 Value      Description<br />
19707 ---------  ------------------------------------------<br />
19708 handles    Array of DOM nodes that trigger dragging<br />
19709            for the element being registered<br />
19710 isHandle   True if the element passed in triggers<br />
19711            dragging itself, else false
19712 </pre>
19713      */
19714         register : function(el, data){
19715             data = data || {};
19716             if(typeof el == "string"){
19717                 el = document.getElementById(el);
19718             }
19719             data.ddel = el;
19720             elements[getId(el)] = data;
19721             if(data.isHandle !== false){
19722                 handles[data.ddel.id] = data;
19723             }
19724             if(data.handles){
19725                 var hs = data.handles;
19726                 for(var i = 0, len = hs.length; i < len; i++){
19727                         handles[getId(hs[i])] = data;
19728                 }
19729             }
19730         },
19731
19732     /**
19733      * Unregister a drag drop element
19734      * @param {String|HTMLElement}  element The id or DOM node to unregister
19735      */
19736         unregister : function(el){
19737             var id = getId(el, false);
19738             var data = elements[id];
19739             if(data){
19740                 delete elements[id];
19741                 if(data.handles){
19742                     var hs = data.handles;
19743                     for(var i = 0, len = hs.length; i < len; i++){
19744                         delete handles[getId(hs[i], false)];
19745                     }
19746                 }
19747             }
19748         },
19749
19750     /**
19751      * Returns the handle registered for a DOM Node by id
19752      * @param {String|HTMLElement} id The DOM node or id to look up
19753      * @return {Object} handle The custom handle data
19754      */
19755         getHandle : function(id){
19756             if(typeof id != "string"){ // must be element?
19757                 id = id.id;
19758             }
19759             return handles[id];
19760         },
19761
19762     /**
19763      * Returns the handle that is registered for the DOM node that is the target of the event
19764      * @param {Event} e The event
19765      * @return {Object} handle The custom handle data
19766      */
19767         getHandleFromEvent : function(e){
19768             var t = Roo.lib.Event.getTarget(e);
19769             return t ? handles[t.id] : null;
19770         },
19771
19772     /**
19773      * Returns a custom data object that is registered for a DOM node by id
19774      * @param {String|HTMLElement} id The DOM node or id to look up
19775      * @return {Object} data The custom data
19776      */
19777         getTarget : function(id){
19778             if(typeof id != "string"){ // must be element?
19779                 id = id.id;
19780             }
19781             return elements[id];
19782         },
19783
19784     /**
19785      * Returns a custom data object that is registered for the DOM node that is the target of the event
19786      * @param {Event} e The event
19787      * @return {Object} data The custom data
19788      */
19789         getTargetFromEvent : function(e){
19790             var t = Roo.lib.Event.getTarget(e);
19791             return t ? elements[t.id] || handles[t.id] : null;
19792         }
19793     };
19794 }();/*
19795  * Based on:
19796  * Ext JS Library 1.1.1
19797  * Copyright(c) 2006-2007, Ext JS, LLC.
19798  *
19799  * Originally Released Under LGPL - original licence link has changed is not relivant.
19800  *
19801  * Fork - LGPL
19802  * <script type="text/javascript">
19803  */
19804  
19805
19806 /**
19807  * @class Roo.dd.StatusProxy
19808  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19809  * default drag proxy used by all Roo.dd components.
19810  * @constructor
19811  * @param {Object} config
19812  */
19813 Roo.dd.StatusProxy = function(config){
19814     Roo.apply(this, config);
19815     this.id = this.id || Roo.id();
19816     this.el = new Roo.Layer({
19817         dh: {
19818             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19819                 {tag: "div", cls: "x-dd-drop-icon"},
19820                 {tag: "div", cls: "x-dd-drag-ghost"}
19821             ]
19822         }, 
19823         shadow: !config || config.shadow !== false
19824     });
19825     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19826     this.dropStatus = this.dropNotAllowed;
19827 };
19828
19829 Roo.dd.StatusProxy.prototype = {
19830     /**
19831      * @cfg {String} dropAllowed
19832      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19833      */
19834     dropAllowed : "x-dd-drop-ok",
19835     /**
19836      * @cfg {String} dropNotAllowed
19837      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19838      */
19839     dropNotAllowed : "x-dd-drop-nodrop",
19840
19841     /**
19842      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19843      * over the current target element.
19844      * @param {String} cssClass The css class for the new drop status indicator image
19845      */
19846     setStatus : function(cssClass){
19847         cssClass = cssClass || this.dropNotAllowed;
19848         if(this.dropStatus != cssClass){
19849             this.el.replaceClass(this.dropStatus, cssClass);
19850             this.dropStatus = cssClass;
19851         }
19852     },
19853
19854     /**
19855      * Resets the status indicator to the default dropNotAllowed value
19856      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19857      */
19858     reset : function(clearGhost){
19859         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19860         this.dropStatus = this.dropNotAllowed;
19861         if(clearGhost){
19862             this.ghost.update("");
19863         }
19864     },
19865
19866     /**
19867      * Updates the contents of the ghost element
19868      * @param {String} html The html that will replace the current innerHTML of the ghost element
19869      */
19870     update : function(html){
19871         if(typeof html == "string"){
19872             this.ghost.update(html);
19873         }else{
19874             this.ghost.update("");
19875             html.style.margin = "0";
19876             this.ghost.dom.appendChild(html);
19877         }
19878         // ensure float = none set?? cant remember why though.
19879         var el = this.ghost.dom.firstChild;
19880                 if(el){
19881                         Roo.fly(el).setStyle('float', 'none');
19882                 }
19883     },
19884     
19885     /**
19886      * Returns the underlying proxy {@link Roo.Layer}
19887      * @return {Roo.Layer} el
19888     */
19889     getEl : function(){
19890         return this.el;
19891     },
19892
19893     /**
19894      * Returns the ghost element
19895      * @return {Roo.Element} el
19896      */
19897     getGhost : function(){
19898         return this.ghost;
19899     },
19900
19901     /**
19902      * Hides the proxy
19903      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19904      */
19905     hide : function(clear){
19906         this.el.hide();
19907         if(clear){
19908             this.reset(true);
19909         }
19910     },
19911
19912     /**
19913      * Stops the repair animation if it's currently running
19914      */
19915     stop : function(){
19916         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19917             this.anim.stop();
19918         }
19919     },
19920
19921     /**
19922      * Displays this proxy
19923      */
19924     show : function(){
19925         this.el.show();
19926     },
19927
19928     /**
19929      * Force the Layer to sync its shadow and shim positions to the element
19930      */
19931     sync : function(){
19932         this.el.sync();
19933     },
19934
19935     /**
19936      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19937      * invalid drop operation by the item being dragged.
19938      * @param {Array} xy The XY position of the element ([x, y])
19939      * @param {Function} callback The function to call after the repair is complete
19940      * @param {Object} scope The scope in which to execute the callback
19941      */
19942     repair : function(xy, callback, scope){
19943         this.callback = callback;
19944         this.scope = scope;
19945         if(xy && this.animRepair !== false){
19946             this.el.addClass("x-dd-drag-repair");
19947             this.el.hideUnders(true);
19948             this.anim = this.el.shift({
19949                 duration: this.repairDuration || .5,
19950                 easing: 'easeOut',
19951                 xy: xy,
19952                 stopFx: true,
19953                 callback: this.afterRepair,
19954                 scope: this
19955             });
19956         }else{
19957             this.afterRepair();
19958         }
19959     },
19960
19961     // private
19962     afterRepair : function(){
19963         this.hide(true);
19964         if(typeof this.callback == "function"){
19965             this.callback.call(this.scope || this);
19966         }
19967         this.callback = null;
19968         this.scope = null;
19969     }
19970 };/*
19971  * Based on:
19972  * Ext JS Library 1.1.1
19973  * Copyright(c) 2006-2007, Ext JS, LLC.
19974  *
19975  * Originally Released Under LGPL - original licence link has changed is not relivant.
19976  *
19977  * Fork - LGPL
19978  * <script type="text/javascript">
19979  */
19980
19981 /**
19982  * @class Roo.dd.DragSource
19983  * @extends Roo.dd.DDProxy
19984  * A simple class that provides the basic implementation needed to make any element draggable.
19985  * @constructor
19986  * @param {String/HTMLElement/Element} el The container element
19987  * @param {Object} config
19988  */
19989 Roo.dd.DragSource = function(el, config){
19990     this.el = Roo.get(el);
19991     this.dragData = {};
19992     
19993     Roo.apply(this, config);
19994     
19995     if(!this.proxy){
19996         this.proxy = new Roo.dd.StatusProxy();
19997     }
19998
19999     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20000           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20001     
20002     this.dragging = false;
20003 };
20004
20005 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20006     /**
20007      * @cfg {String} dropAllowed
20008      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20009      */
20010     dropAllowed : "x-dd-drop-ok",
20011     /**
20012      * @cfg {String} dropNotAllowed
20013      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20014      */
20015     dropNotAllowed : "x-dd-drop-nodrop",
20016
20017     /**
20018      * Returns the data object associated with this drag source
20019      * @return {Object} data An object containing arbitrary data
20020      */
20021     getDragData : function(e){
20022         return this.dragData;
20023     },
20024
20025     // private
20026     onDragEnter : function(e, id){
20027         var target = Roo.dd.DragDropMgr.getDDById(id);
20028         this.cachedTarget = target;
20029         if(this.beforeDragEnter(target, e, id) !== false){
20030             if(target.isNotifyTarget){
20031                 var status = target.notifyEnter(this, e, this.dragData);
20032                 this.proxy.setStatus(status);
20033             }else{
20034                 this.proxy.setStatus(this.dropAllowed);
20035             }
20036             
20037             if(this.afterDragEnter){
20038                 /**
20039                  * An empty function by default, but provided so that you can perform a custom action
20040                  * when the dragged item enters the drop target by providing an implementation.
20041                  * @param {Roo.dd.DragDrop} target The drop target
20042                  * @param {Event} e The event object
20043                  * @param {String} id The id of the dragged element
20044                  * @method afterDragEnter
20045                  */
20046                 this.afterDragEnter(target, e, id);
20047             }
20048         }
20049     },
20050
20051     /**
20052      * An empty function by default, but provided so that you can perform a custom action
20053      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20054      * @param {Roo.dd.DragDrop} target The drop target
20055      * @param {Event} e The event object
20056      * @param {String} id The id of the dragged element
20057      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20058      */
20059     beforeDragEnter : function(target, e, id){
20060         return true;
20061     },
20062
20063     // private
20064     alignElWithMouse: function() {
20065         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20066         this.proxy.sync();
20067     },
20068
20069     // private
20070     onDragOver : function(e, id){
20071         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20072         if(this.beforeDragOver(target, e, id) !== false){
20073             if(target.isNotifyTarget){
20074                 var status = target.notifyOver(this, e, this.dragData);
20075                 this.proxy.setStatus(status);
20076             }
20077
20078             if(this.afterDragOver){
20079                 /**
20080                  * An empty function by default, but provided so that you can perform a custom action
20081                  * while the dragged item is over the drop target by providing an implementation.
20082                  * @param {Roo.dd.DragDrop} target The drop target
20083                  * @param {Event} e The event object
20084                  * @param {String} id The id of the dragged element
20085                  * @method afterDragOver
20086                  */
20087                 this.afterDragOver(target, e, id);
20088             }
20089         }
20090     },
20091
20092     /**
20093      * An empty function by default, but provided so that you can perform a custom action
20094      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20095      * @param {Roo.dd.DragDrop} target The drop target
20096      * @param {Event} e The event object
20097      * @param {String} id The id of the dragged element
20098      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20099      */
20100     beforeDragOver : function(target, e, id){
20101         return true;
20102     },
20103
20104     // private
20105     onDragOut : function(e, id){
20106         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20107         if(this.beforeDragOut(target, e, id) !== false){
20108             if(target.isNotifyTarget){
20109                 target.notifyOut(this, e, this.dragData);
20110             }
20111             this.proxy.reset();
20112             if(this.afterDragOut){
20113                 /**
20114                  * An empty function by default, but provided so that you can perform a custom action
20115                  * after the dragged item is dragged out of the target without dropping.
20116                  * @param {Roo.dd.DragDrop} target The drop target
20117                  * @param {Event} e The event object
20118                  * @param {String} id The id of the dragged element
20119                  * @method afterDragOut
20120                  */
20121                 this.afterDragOut(target, e, id);
20122             }
20123         }
20124         this.cachedTarget = null;
20125     },
20126
20127     /**
20128      * An empty function by default, but provided so that you can perform a custom action before the dragged
20129      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20130      * @param {Roo.dd.DragDrop} target The drop target
20131      * @param {Event} e The event object
20132      * @param {String} id The id of the dragged element
20133      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20134      */
20135     beforeDragOut : function(target, e, id){
20136         return true;
20137     },
20138     
20139     // private
20140     onDragDrop : function(e, id){
20141         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20142         if(this.beforeDragDrop(target, e, id) !== false){
20143             if(target.isNotifyTarget){
20144                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20145                     this.onValidDrop(target, e, id);
20146                 }else{
20147                     this.onInvalidDrop(target, e, id);
20148                 }
20149             }else{
20150                 this.onValidDrop(target, e, id);
20151             }
20152             
20153             if(this.afterDragDrop){
20154                 /**
20155                  * An empty function by default, but provided so that you can perform a custom action
20156                  * after a valid drag drop has occurred by providing an implementation.
20157                  * @param {Roo.dd.DragDrop} target The drop target
20158                  * @param {Event} e The event object
20159                  * @param {String} id The id of the dropped element
20160                  * @method afterDragDrop
20161                  */
20162                 this.afterDragDrop(target, e, id);
20163             }
20164         }
20165         delete this.cachedTarget;
20166     },
20167
20168     /**
20169      * An empty function by default, but provided so that you can perform a custom action before the dragged
20170      * item is dropped onto the target and optionally cancel the onDragDrop.
20171      * @param {Roo.dd.DragDrop} target The drop target
20172      * @param {Event} e The event object
20173      * @param {String} id The id of the dragged element
20174      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20175      */
20176     beforeDragDrop : function(target, e, id){
20177         return true;
20178     },
20179
20180     // private
20181     onValidDrop : function(target, e, id){
20182         this.hideProxy();
20183         if(this.afterValidDrop){
20184             /**
20185              * An empty function by default, but provided so that you can perform a custom action
20186              * after a valid drop has occurred by providing an implementation.
20187              * @param {Object} target The target DD 
20188              * @param {Event} e The event object
20189              * @param {String} id The id of the dropped element
20190              * @method afterInvalidDrop
20191              */
20192             this.afterValidDrop(target, e, id);
20193         }
20194     },
20195
20196     // private
20197     getRepairXY : function(e, data){
20198         return this.el.getXY();  
20199     },
20200
20201     // private
20202     onInvalidDrop : function(target, e, id){
20203         this.beforeInvalidDrop(target, e, id);
20204         if(this.cachedTarget){
20205             if(this.cachedTarget.isNotifyTarget){
20206                 this.cachedTarget.notifyOut(this, e, this.dragData);
20207             }
20208             this.cacheTarget = null;
20209         }
20210         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20211
20212         if(this.afterInvalidDrop){
20213             /**
20214              * An empty function by default, but provided so that you can perform a custom action
20215              * after an invalid drop has occurred by providing an implementation.
20216              * @param {Event} e The event object
20217              * @param {String} id The id of the dropped element
20218              * @method afterInvalidDrop
20219              */
20220             this.afterInvalidDrop(e, id);
20221         }
20222     },
20223
20224     // private
20225     afterRepair : function(){
20226         if(Roo.enableFx){
20227             this.el.highlight(this.hlColor || "c3daf9");
20228         }
20229         this.dragging = false;
20230     },
20231
20232     /**
20233      * An empty function by default, but provided so that you can perform a custom action after an invalid
20234      * drop has occurred.
20235      * @param {Roo.dd.DragDrop} target The drop target
20236      * @param {Event} e The event object
20237      * @param {String} id The id of the dragged element
20238      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20239      */
20240     beforeInvalidDrop : function(target, e, id){
20241         return true;
20242     },
20243
20244     // private
20245     handleMouseDown : function(e){
20246         if(this.dragging) {
20247             return;
20248         }
20249         var data = this.getDragData(e);
20250         if(data && this.onBeforeDrag(data, e) !== false){
20251             this.dragData = data;
20252             this.proxy.stop();
20253             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20254         } 
20255     },
20256
20257     /**
20258      * An empty function by default, but provided so that you can perform a custom action before the initial
20259      * drag event begins and optionally cancel it.
20260      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20261      * @param {Event} e The event object
20262      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20263      */
20264     onBeforeDrag : function(data, e){
20265         return true;
20266     },
20267
20268     /**
20269      * An empty function by default, but provided so that you can perform a custom action once the initial
20270      * drag event has begun.  The drag cannot be canceled from this function.
20271      * @param {Number} x The x position of the click on the dragged object
20272      * @param {Number} y The y position of the click on the dragged object
20273      */
20274     onStartDrag : Roo.emptyFn,
20275
20276     // private - YUI override
20277     startDrag : function(x, y){
20278         this.proxy.reset();
20279         this.dragging = true;
20280         this.proxy.update("");
20281         this.onInitDrag(x, y);
20282         this.proxy.show();
20283     },
20284
20285     // private
20286     onInitDrag : function(x, y){
20287         var clone = this.el.dom.cloneNode(true);
20288         clone.id = Roo.id(); // prevent duplicate ids
20289         this.proxy.update(clone);
20290         this.onStartDrag(x, y);
20291         return true;
20292     },
20293
20294     /**
20295      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20296      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20297      */
20298     getProxy : function(){
20299         return this.proxy;  
20300     },
20301
20302     /**
20303      * Hides the drag source's {@link Roo.dd.StatusProxy}
20304      */
20305     hideProxy : function(){
20306         this.proxy.hide();  
20307         this.proxy.reset(true);
20308         this.dragging = false;
20309     },
20310
20311     // private
20312     triggerCacheRefresh : function(){
20313         Roo.dd.DDM.refreshCache(this.groups);
20314     },
20315
20316     // private - override to prevent hiding
20317     b4EndDrag: function(e) {
20318     },
20319
20320     // private - override to prevent moving
20321     endDrag : function(e){
20322         this.onEndDrag(this.dragData, e);
20323     },
20324
20325     // private
20326     onEndDrag : function(data, e){
20327     },
20328     
20329     // private - pin to cursor
20330     autoOffset : function(x, y) {
20331         this.setDelta(-12, -20);
20332     }    
20333 });/*
20334  * Based on:
20335  * Ext JS Library 1.1.1
20336  * Copyright(c) 2006-2007, Ext JS, LLC.
20337  *
20338  * Originally Released Under LGPL - original licence link has changed is not relivant.
20339  *
20340  * Fork - LGPL
20341  * <script type="text/javascript">
20342  */
20343
20344
20345 /**
20346  * @class Roo.dd.DropTarget
20347  * @extends Roo.dd.DDTarget
20348  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20349  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20350  * @constructor
20351  * @param {String/HTMLElement/Element} el The container element
20352  * @param {Object} config
20353  */
20354 Roo.dd.DropTarget = function(el, config){
20355     this.el = Roo.get(el);
20356     
20357     var listeners = false; ;
20358     if (config && config.listeners) {
20359         listeners= config.listeners;
20360         delete config.listeners;
20361     }
20362     Roo.apply(this, config);
20363     
20364     if(this.containerScroll){
20365         Roo.dd.ScrollManager.register(this.el);
20366     }
20367     this.addEvents( {
20368          /**
20369          * @scope Roo.dd.DropTarget
20370          */
20371          
20372          /**
20373          * @event enter
20374          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20375          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20376          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20377          * 
20378          * IMPORTANT : it should set this.overClass and this.dropAllowed
20379          * 
20380          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20381          * @param {Event} e The event
20382          * @param {Object} data An object containing arbitrary data supplied by the drag source
20383          */
20384         "enter" : true,
20385         
20386          /**
20387          * @event over
20388          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20389          * This method will be called on every mouse movement while the drag source is over the drop target.
20390          * This default implementation simply returns the dropAllowed config value.
20391          * 
20392          * IMPORTANT : it should set this.dropAllowed
20393          * 
20394          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20395          * @param {Event} e The event
20396          * @param {Object} data An object containing arbitrary data supplied by the drag source
20397          
20398          */
20399         "over" : true,
20400         /**
20401          * @event out
20402          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20403          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20404          * overClass (if any) from the drop element.
20405          * 
20406          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20407          * @param {Event} e The event
20408          * @param {Object} data An object containing arbitrary data supplied by the drag source
20409          */
20410          "out" : true,
20411          
20412         /**
20413          * @event drop
20414          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20415          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20416          * implementation that does something to process the drop event and returns true so that the drag source's
20417          * repair action does not run.
20418          * 
20419          * IMPORTANT : it should set this.success
20420          * 
20421          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20422          * @param {Event} e The event
20423          * @param {Object} data An object containing arbitrary data supplied by the drag source
20424         */
20425          "drop" : true
20426     });
20427             
20428      
20429     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20430         this.el.dom, 
20431         this.ddGroup || this.group,
20432         {
20433             isTarget: true,
20434             listeners : listeners || {} 
20435            
20436         
20437         }
20438     );
20439
20440 };
20441
20442 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20443     /**
20444      * @cfg {String} overClass
20445      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20446      */
20447      /**
20448      * @cfg {String} ddGroup
20449      * The drag drop group to handle drop events for
20450      */
20451      
20452     /**
20453      * @cfg {String} dropAllowed
20454      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20455      */
20456     dropAllowed : "x-dd-drop-ok",
20457     /**
20458      * @cfg {String} dropNotAllowed
20459      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20460      */
20461     dropNotAllowed : "x-dd-drop-nodrop",
20462     /**
20463      * @cfg {boolean} success
20464      * set this after drop listener.. 
20465      */
20466     success : false,
20467     /**
20468      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20469      * if the drop point is valid for over/enter..
20470      */
20471     valid : false,
20472     // private
20473     isTarget : true,
20474
20475     // private
20476     isNotifyTarget : true,
20477     
20478     /**
20479      * @hide
20480      */
20481     notifyEnter : function(dd, e, data)
20482     {
20483         this.valid = true;
20484         this.fireEvent('enter', dd, e, data);
20485         if(this.overClass){
20486             this.el.addClass(this.overClass);
20487         }
20488         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20489             this.valid ? this.dropAllowed : this.dropNotAllowed
20490         );
20491     },
20492
20493     /**
20494      * @hide
20495      */
20496     notifyOver : function(dd, e, data)
20497     {
20498         this.valid = true;
20499         this.fireEvent('over', dd, e, data);
20500         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20501             this.valid ? this.dropAllowed : this.dropNotAllowed
20502         );
20503     },
20504
20505     /**
20506      * @hide
20507      */
20508     notifyOut : function(dd, e, data)
20509     {
20510         this.fireEvent('out', dd, e, data);
20511         if(this.overClass){
20512             this.el.removeClass(this.overClass);
20513         }
20514     },
20515
20516     /**
20517      * @hide
20518      */
20519     notifyDrop : function(dd, e, data)
20520     {
20521         this.success = false;
20522         this.fireEvent('drop', dd, e, data);
20523         return this.success;
20524     }
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535
20536
20537 /**
20538  * @class Roo.dd.DragZone
20539  * @extends Roo.dd.DragSource
20540  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20541  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20542  * @constructor
20543  * @param {String/HTMLElement/Element} el The container element
20544  * @param {Object} config
20545  */
20546 Roo.dd.DragZone = function(el, config){
20547     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20548     if(this.containerScroll){
20549         Roo.dd.ScrollManager.register(this.el);
20550     }
20551 };
20552
20553 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20554     /**
20555      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20556      * for auto scrolling during drag operations.
20557      */
20558     /**
20559      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20560      * method after a failed drop (defaults to "c3daf9" - light blue)
20561      */
20562
20563     /**
20564      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20565      * for a valid target to drag based on the mouse down. Override this method
20566      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20567      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20568      * @param {EventObject} e The mouse down event
20569      * @return {Object} The dragData
20570      */
20571     getDragData : function(e){
20572         return Roo.dd.Registry.getHandleFromEvent(e);
20573     },
20574     
20575     /**
20576      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20577      * this.dragData.ddel
20578      * @param {Number} x The x position of the click on the dragged object
20579      * @param {Number} y The y position of the click on the dragged object
20580      * @return {Boolean} true to continue the drag, false to cancel
20581      */
20582     onInitDrag : function(x, y){
20583         this.proxy.update(this.dragData.ddel.cloneNode(true));
20584         this.onStartDrag(x, y);
20585         return true;
20586     },
20587     
20588     /**
20589      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20590      */
20591     afterRepair : function(){
20592         if(Roo.enableFx){
20593             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20594         }
20595         this.dragging = false;
20596     },
20597
20598     /**
20599      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20600      * the XY of this.dragData.ddel
20601      * @param {EventObject} e The mouse up event
20602      * @return {Array} The xy location (e.g. [100, 200])
20603      */
20604     getRepairXY : function(e){
20605         return Roo.Element.fly(this.dragData.ddel).getXY();  
20606     }
20607 });/*
20608  * Based on:
20609  * Ext JS Library 1.1.1
20610  * Copyright(c) 2006-2007, Ext JS, LLC.
20611  *
20612  * Originally Released Under LGPL - original licence link has changed is not relivant.
20613  *
20614  * Fork - LGPL
20615  * <script type="text/javascript">
20616  */
20617 /**
20618  * @class Roo.dd.DropZone
20619  * @extends Roo.dd.DropTarget
20620  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20621  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20622  * @constructor
20623  * @param {String/HTMLElement/Element} el The container element
20624  * @param {Object} config
20625  */
20626 Roo.dd.DropZone = function(el, config){
20627     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20628 };
20629
20630 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20631     /**
20632      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20633      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20634      * provide your own custom lookup.
20635      * @param {Event} e The event
20636      * @return {Object} data The custom data
20637      */
20638     getTargetFromEvent : function(e){
20639         return Roo.dd.Registry.getTargetFromEvent(e);
20640     },
20641
20642     /**
20643      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20644      * that it has registered.  This method has no default implementation and should be overridden to provide
20645      * node-specific processing if necessary.
20646      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20647      * {@link #getTargetFromEvent} for this node)
20648      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20649      * @param {Event} e The event
20650      * @param {Object} data An object containing arbitrary data supplied by the drag source
20651      */
20652     onNodeEnter : function(n, dd, e, data){
20653         
20654     },
20655
20656     /**
20657      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20658      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20659      * overridden to provide the proper feedback.
20660      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20661      * {@link #getTargetFromEvent} for this node)
20662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20663      * @param {Event} e The event
20664      * @param {Object} data An object containing arbitrary data supplied by the drag source
20665      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20666      * underlying {@link Roo.dd.StatusProxy} can be updated
20667      */
20668     onNodeOver : function(n, dd, e, data){
20669         return this.dropAllowed;
20670     },
20671
20672     /**
20673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20674      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20675      * node-specific processing if necessary.
20676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20677      * {@link #getTargetFromEvent} for this node)
20678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20679      * @param {Event} e The event
20680      * @param {Object} data An object containing arbitrary data supplied by the drag source
20681      */
20682     onNodeOut : function(n, dd, e, data){
20683         
20684     },
20685
20686     /**
20687      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20688      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20689      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20691      * {@link #getTargetFromEvent} for this node)
20692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20693      * @param {Event} e The event
20694      * @param {Object} data An object containing arbitrary data supplied by the drag source
20695      * @return {Boolean} True if the drop was valid, else false
20696      */
20697     onNodeDrop : function(n, dd, e, data){
20698         return false;
20699     },
20700
20701     /**
20702      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20703      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20704      * it should be overridden to provide the proper feedback if necessary.
20705      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20706      * @param {Event} e The event
20707      * @param {Object} data An object containing arbitrary data supplied by the drag source
20708      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20709      * underlying {@link Roo.dd.StatusProxy} can be updated
20710      */
20711     onContainerOver : function(dd, e, data){
20712         return this.dropNotAllowed;
20713     },
20714
20715     /**
20716      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20717      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20718      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20719      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20720      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20721      * @param {Event} e The event
20722      * @param {Object} data An object containing arbitrary data supplied by the drag source
20723      * @return {Boolean} True if the drop was valid, else false
20724      */
20725     onContainerDrop : function(dd, e, data){
20726         return false;
20727     },
20728
20729     /**
20730      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20731      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20732      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20733      * you should override this method and provide a custom implementation.
20734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20735      * @param {Event} e The event
20736      * @param {Object} data An object containing arbitrary data supplied by the drag source
20737      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20738      * underlying {@link Roo.dd.StatusProxy} can be updated
20739      */
20740     notifyEnter : function(dd, e, data){
20741         return this.dropNotAllowed;
20742     },
20743
20744     /**
20745      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20746      * This method will be called on every mouse movement while the drag source is over the drop zone.
20747      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20748      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20749      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20750      * registered node, it will call {@link #onContainerOver}.
20751      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20752      * @param {Event} e The event
20753      * @param {Object} data An object containing arbitrary data supplied by the drag source
20754      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20755      * underlying {@link Roo.dd.StatusProxy} can be updated
20756      */
20757     notifyOver : function(dd, e, data){
20758         var n = this.getTargetFromEvent(e);
20759         if(!n){ // not over valid drop target
20760             if(this.lastOverNode){
20761                 this.onNodeOut(this.lastOverNode, dd, e, data);
20762                 this.lastOverNode = null;
20763             }
20764             return this.onContainerOver(dd, e, data);
20765         }
20766         if(this.lastOverNode != n){
20767             if(this.lastOverNode){
20768                 this.onNodeOut(this.lastOverNode, dd, e, data);
20769             }
20770             this.onNodeEnter(n, dd, e, data);
20771             this.lastOverNode = n;
20772         }
20773         return this.onNodeOver(n, dd, e, data);
20774     },
20775
20776     /**
20777      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20778      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20779      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20780      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20781      * @param {Event} e The event
20782      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20783      */
20784     notifyOut : function(dd, e, data){
20785         if(this.lastOverNode){
20786             this.onNodeOut(this.lastOverNode, dd, e, data);
20787             this.lastOverNode = null;
20788         }
20789     },
20790
20791     /**
20792      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20793      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20794      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20795      * otherwise it will call {@link #onContainerDrop}.
20796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20797      * @param {Event} e The event
20798      * @param {Object} data An object containing arbitrary data supplied by the drag source
20799      * @return {Boolean} True if the drop was valid, else false
20800      */
20801     notifyDrop : function(dd, e, data){
20802         if(this.lastOverNode){
20803             this.onNodeOut(this.lastOverNode, dd, e, data);
20804             this.lastOverNode = null;
20805         }
20806         var n = this.getTargetFromEvent(e);
20807         return n ?
20808             this.onNodeDrop(n, dd, e, data) :
20809             this.onContainerDrop(dd, e, data);
20810     },
20811
20812     // private
20813     triggerCacheRefresh : function(){
20814         Roo.dd.DDM.refreshCache(this.groups);
20815     }  
20816 });/*
20817  * Based on:
20818  * Ext JS Library 1.1.1
20819  * Copyright(c) 2006-2007, Ext JS, LLC.
20820  *
20821  * Originally Released Under LGPL - original licence link has changed is not relivant.
20822  *
20823  * Fork - LGPL
20824  * <script type="text/javascript">
20825  */
20826
20827
20828 /**
20829  * @class Roo.data.SortTypes
20830  * @singleton
20831  * Defines the default sorting (casting?) comparison functions used when sorting data.
20832  */
20833 Roo.data.SortTypes = {
20834     /**
20835      * Default sort that does nothing
20836      * @param {Mixed} s The value being converted
20837      * @return {Mixed} The comparison value
20838      */
20839     none : function(s){
20840         return s;
20841     },
20842     
20843     /**
20844      * The regular expression used to strip tags
20845      * @type {RegExp}
20846      * @property
20847      */
20848     stripTagsRE : /<\/?[^>]+>/gi,
20849     
20850     /**
20851      * Strips all HTML tags to sort on text only
20852      * @param {Mixed} s The value being converted
20853      * @return {String} The comparison value
20854      */
20855     asText : function(s){
20856         return String(s).replace(this.stripTagsRE, "");
20857     },
20858     
20859     /**
20860      * Strips all HTML tags to sort on text only - Case insensitive
20861      * @param {Mixed} s The value being converted
20862      * @return {String} The comparison value
20863      */
20864     asUCText : function(s){
20865         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20866     },
20867     
20868     /**
20869      * Case insensitive string
20870      * @param {Mixed} s The value being converted
20871      * @return {String} The comparison value
20872      */
20873     asUCString : function(s) {
20874         return String(s).toUpperCase();
20875     },
20876     
20877     /**
20878      * Date sorting
20879      * @param {Mixed} s The value being converted
20880      * @return {Number} The comparison value
20881      */
20882     asDate : function(s) {
20883         if(!s){
20884             return 0;
20885         }
20886         if(s instanceof Date){
20887             return s.getTime();
20888         }
20889         return Date.parse(String(s));
20890     },
20891     
20892     /**
20893      * Float sorting
20894      * @param {Mixed} s The value being converted
20895      * @return {Float} The comparison value
20896      */
20897     asFloat : function(s) {
20898         var val = parseFloat(String(s).replace(/,/g, ""));
20899         if(isNaN(val)) val = 0;
20900         return val;
20901     },
20902     
20903     /**
20904      * Integer sorting
20905      * @param {Mixed} s The value being converted
20906      * @return {Number} The comparison value
20907      */
20908     asInt : function(s) {
20909         var val = parseInt(String(s).replace(/,/g, ""));
20910         if(isNaN(val)) val = 0;
20911         return val;
20912     }
20913 };/*
20914  * Based on:
20915  * Ext JS Library 1.1.1
20916  * Copyright(c) 2006-2007, Ext JS, LLC.
20917  *
20918  * Originally Released Under LGPL - original licence link has changed is not relivant.
20919  *
20920  * Fork - LGPL
20921  * <script type="text/javascript">
20922  */
20923
20924 /**
20925 * @class Roo.data.Record
20926  * Instances of this class encapsulate both record <em>definition</em> information, and record
20927  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20928  * to access Records cached in an {@link Roo.data.Store} object.<br>
20929  * <p>
20930  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20931  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20932  * objects.<br>
20933  * <p>
20934  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20935  * @constructor
20936  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20937  * {@link #create}. The parameters are the same.
20938  * @param {Array} data An associative Array of data values keyed by the field name.
20939  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20940  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20941  * not specified an integer id is generated.
20942  */
20943 Roo.data.Record = function(data, id){
20944     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20945     this.data = data;
20946 };
20947
20948 /**
20949  * Generate a constructor for a specific record layout.
20950  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20951  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20952  * Each field definition object may contain the following properties: <ul>
20953  * <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,
20954  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20955  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20956  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20957  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20958  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20959  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20960  * this may be omitted.</p></li>
20961  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20962  * <ul><li>auto (Default, implies no conversion)</li>
20963  * <li>string</li>
20964  * <li>int</li>
20965  * <li>float</li>
20966  * <li>boolean</li>
20967  * <li>date</li></ul></p></li>
20968  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20969  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20970  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20971  * by the Reader into an object that will be stored in the Record. It is passed the
20972  * following parameters:<ul>
20973  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20974  * </ul></p></li>
20975  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20976  * </ul>
20977  * <br>usage:<br><pre><code>
20978 var TopicRecord = Roo.data.Record.create(
20979     {name: 'title', mapping: 'topic_title'},
20980     {name: 'author', mapping: 'username'},
20981     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20982     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20983     {name: 'lastPoster', mapping: 'user2'},
20984     {name: 'excerpt', mapping: 'post_text'}
20985 );
20986
20987 var myNewRecord = new TopicRecord({
20988     title: 'Do my job please',
20989     author: 'noobie',
20990     totalPosts: 1,
20991     lastPost: new Date(),
20992     lastPoster: 'Animal',
20993     excerpt: 'No way dude!'
20994 });
20995 myStore.add(myNewRecord);
20996 </code></pre>
20997  * @method create
20998  * @static
20999  */
21000 Roo.data.Record.create = function(o){
21001     var f = function(){
21002         f.superclass.constructor.apply(this, arguments);
21003     };
21004     Roo.extend(f, Roo.data.Record);
21005     var p = f.prototype;
21006     p.fields = new Roo.util.MixedCollection(false, function(field){
21007         return field.name;
21008     });
21009     for(var i = 0, len = o.length; i < len; i++){
21010         p.fields.add(new Roo.data.Field(o[i]));
21011     }
21012     f.getField = function(name){
21013         return p.fields.get(name);  
21014     };
21015     return f;
21016 };
21017
21018 Roo.data.Record.AUTO_ID = 1000;
21019 Roo.data.Record.EDIT = 'edit';
21020 Roo.data.Record.REJECT = 'reject';
21021 Roo.data.Record.COMMIT = 'commit';
21022
21023 Roo.data.Record.prototype = {
21024     /**
21025      * Readonly flag - true if this record has been modified.
21026      * @type Boolean
21027      */
21028     dirty : false,
21029     editing : false,
21030     error: null,
21031     modified: null,
21032
21033     // private
21034     join : function(store){
21035         this.store = store;
21036     },
21037
21038     /**
21039      * Set the named field to the specified value.
21040      * @param {String} name The name of the field to set.
21041      * @param {Object} value The value to set the field to.
21042      */
21043     set : function(name, value){
21044         if(this.data[name] == value){
21045             return;
21046         }
21047         this.dirty = true;
21048         if(!this.modified){
21049             this.modified = {};
21050         }
21051         if(typeof this.modified[name] == 'undefined'){
21052             this.modified[name] = this.data[name];
21053         }
21054         this.data[name] = value;
21055         if(!this.editing && this.store){
21056             this.store.afterEdit(this);
21057         }       
21058     },
21059
21060     /**
21061      * Get the value of the named field.
21062      * @param {String} name The name of the field to get the value of.
21063      * @return {Object} The value of the field.
21064      */
21065     get : function(name){
21066         return this.data[name]; 
21067     },
21068
21069     // private
21070     beginEdit : function(){
21071         this.editing = true;
21072         this.modified = {}; 
21073     },
21074
21075     // private
21076     cancelEdit : function(){
21077         this.editing = false;
21078         delete this.modified;
21079     },
21080
21081     // private
21082     endEdit : function(){
21083         this.editing = false;
21084         if(this.dirty && this.store){
21085             this.store.afterEdit(this);
21086         }
21087     },
21088
21089     /**
21090      * Usually called by the {@link Roo.data.Store} which owns the Record.
21091      * Rejects all changes made to the Record since either creation, or the last commit operation.
21092      * Modified fields are reverted to their original values.
21093      * <p>
21094      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21095      * of reject operations.
21096      */
21097     reject : function(){
21098         var m = this.modified;
21099         for(var n in m){
21100             if(typeof m[n] != "function"){
21101                 this.data[n] = m[n];
21102             }
21103         }
21104         this.dirty = false;
21105         delete this.modified;
21106         this.editing = false;
21107         if(this.store){
21108             this.store.afterReject(this);
21109         }
21110     },
21111
21112     /**
21113      * Usually called by the {@link Roo.data.Store} which owns the Record.
21114      * Commits all changes made to the Record since either creation, or the last commit operation.
21115      * <p>
21116      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21117      * of commit operations.
21118      */
21119     commit : function(){
21120         this.dirty = false;
21121         delete this.modified;
21122         this.editing = false;
21123         if(this.store){
21124             this.store.afterCommit(this);
21125         }
21126     },
21127
21128     // private
21129     hasError : function(){
21130         return this.error != null;
21131     },
21132
21133     // private
21134     clearError : function(){
21135         this.error = null;
21136     },
21137
21138     /**
21139      * Creates a copy of this record.
21140      * @param {String} id (optional) A new record id if you don't want to use this record's id
21141      * @return {Record}
21142      */
21143     copy : function(newId) {
21144         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21145     }
21146 };/*
21147  * Based on:
21148  * Ext JS Library 1.1.1
21149  * Copyright(c) 2006-2007, Ext JS, LLC.
21150  *
21151  * Originally Released Under LGPL - original licence link has changed is not relivant.
21152  *
21153  * Fork - LGPL
21154  * <script type="text/javascript">
21155  */
21156
21157
21158
21159 /**
21160  * @class Roo.data.Store
21161  * @extends Roo.util.Observable
21162  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21163  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21164  * <p>
21165  * 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
21166  * has no knowledge of the format of the data returned by the Proxy.<br>
21167  * <p>
21168  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21169  * instances from the data object. These records are cached and made available through accessor functions.
21170  * @constructor
21171  * Creates a new Store.
21172  * @param {Object} config A config object containing the objects needed for the Store to access data,
21173  * and read the data into Records.
21174  */
21175 Roo.data.Store = function(config){
21176     this.data = new Roo.util.MixedCollection(false);
21177     this.data.getKey = function(o){
21178         return o.id;
21179     };
21180     this.baseParams = {};
21181     // private
21182     this.paramNames = {
21183         "start" : "start",
21184         "limit" : "limit",
21185         "sort" : "sort",
21186         "dir" : "dir",
21187         "multisort" : "_multisort"
21188     };
21189
21190     if(config && config.data){
21191         this.inlineData = config.data;
21192         delete config.data;
21193     }
21194
21195     Roo.apply(this, config);
21196     
21197     if(this.reader){ // reader passed
21198         this.reader = Roo.factory(this.reader, Roo.data);
21199         this.reader.xmodule = this.xmodule || false;
21200         if(!this.recordType){
21201             this.recordType = this.reader.recordType;
21202         }
21203         if(this.reader.onMetaChange){
21204             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21205         }
21206     }
21207
21208     if(this.recordType){
21209         this.fields = this.recordType.prototype.fields;
21210     }
21211     this.modified = [];
21212
21213     this.addEvents({
21214         /**
21215          * @event datachanged
21216          * Fires when the data cache has changed, and a widget which is using this Store
21217          * as a Record cache should refresh its view.
21218          * @param {Store} this
21219          */
21220         datachanged : true,
21221         /**
21222          * @event metachange
21223          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21224          * @param {Store} this
21225          * @param {Object} meta The JSON metadata
21226          */
21227         metachange : true,
21228         /**
21229          * @event add
21230          * Fires when Records have been added to the Store
21231          * @param {Store} this
21232          * @param {Roo.data.Record[]} records The array of Records added
21233          * @param {Number} index The index at which the record(s) were added
21234          */
21235         add : true,
21236         /**
21237          * @event remove
21238          * Fires when a Record has been removed from the Store
21239          * @param {Store} this
21240          * @param {Roo.data.Record} record The Record that was removed
21241          * @param {Number} index The index at which the record was removed
21242          */
21243         remove : true,
21244         /**
21245          * @event update
21246          * Fires when a Record has been updated
21247          * @param {Store} this
21248          * @param {Roo.data.Record} record The Record that was updated
21249          * @param {String} operation The update operation being performed.  Value may be one of:
21250          * <pre><code>
21251  Roo.data.Record.EDIT
21252  Roo.data.Record.REJECT
21253  Roo.data.Record.COMMIT
21254          * </code></pre>
21255          */
21256         update : true,
21257         /**
21258          * @event clear
21259          * Fires when the data cache has been cleared.
21260          * @param {Store} this
21261          */
21262         clear : true,
21263         /**
21264          * @event beforeload
21265          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21266          * the load action will be canceled.
21267          * @param {Store} this
21268          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21269          */
21270         beforeload : true,
21271         /**
21272          * @event beforeloadadd
21273          * Fires after a new set of Records has been loaded.
21274          * @param {Store} this
21275          * @param {Roo.data.Record[]} records The Records that were loaded
21276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21277          */
21278         beforeloadadd : true,
21279         /**
21280          * @event load
21281          * Fires after a new set of Records has been loaded, before they are added to the store.
21282          * @param {Store} this
21283          * @param {Roo.data.Record[]} records The Records that were loaded
21284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21285          * @params {Object} return from reader
21286          */
21287         load : true,
21288         /**
21289          * @event loadexception
21290          * Fires if an exception occurs in the Proxy during loading.
21291          * Called with the signature of the Proxy's "loadexception" event.
21292          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21293          * 
21294          * @param {Proxy} 
21295          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21296          * @param {Object} load options 
21297          * @param {Object} jsonData from your request (normally this contains the Exception)
21298          */
21299         loadexception : true
21300     });
21301     
21302     if(this.proxy){
21303         this.proxy = Roo.factory(this.proxy, Roo.data);
21304         this.proxy.xmodule = this.xmodule || false;
21305         this.relayEvents(this.proxy,  ["loadexception"]);
21306     }
21307     this.sortToggle = {};
21308     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21309
21310     Roo.data.Store.superclass.constructor.call(this);
21311
21312     if(this.inlineData){
21313         this.loadData(this.inlineData);
21314         delete this.inlineData;
21315     }
21316 };
21317
21318 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21319      /**
21320     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21321     * without a remote query - used by combo/forms at present.
21322     */
21323     
21324     /**
21325     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21326     */
21327     /**
21328     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21329     */
21330     /**
21331     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21332     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21333     */
21334     /**
21335     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21336     * on any HTTP request
21337     */
21338     /**
21339     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21340     */
21341     /**
21342     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21343     */
21344     multiSort: false,
21345     /**
21346     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21347     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21348     */
21349     remoteSort : false,
21350
21351     /**
21352     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21353      * loaded or when a record is removed. (defaults to false).
21354     */
21355     pruneModifiedRecords : false,
21356
21357     // private
21358     lastOptions : null,
21359
21360     /**
21361      * Add Records to the Store and fires the add event.
21362      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21363      */
21364     add : function(records){
21365         records = [].concat(records);
21366         for(var i = 0, len = records.length; i < len; i++){
21367             records[i].join(this);
21368         }
21369         var index = this.data.length;
21370         this.data.addAll(records);
21371         this.fireEvent("add", this, records, index);
21372     },
21373
21374     /**
21375      * Remove a Record from the Store and fires the remove event.
21376      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21377      */
21378     remove : function(record){
21379         var index = this.data.indexOf(record);
21380         this.data.removeAt(index);
21381         if(this.pruneModifiedRecords){
21382             this.modified.remove(record);
21383         }
21384         this.fireEvent("remove", this, record, index);
21385     },
21386
21387     /**
21388      * Remove all Records from the Store and fires the clear event.
21389      */
21390     removeAll : function(){
21391         this.data.clear();
21392         if(this.pruneModifiedRecords){
21393             this.modified = [];
21394         }
21395         this.fireEvent("clear", this);
21396     },
21397
21398     /**
21399      * Inserts Records to the Store at the given index and fires the add event.
21400      * @param {Number} index The start index at which to insert the passed Records.
21401      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21402      */
21403     insert : function(index, records){
21404         records = [].concat(records);
21405         for(var i = 0, len = records.length; i < len; i++){
21406             this.data.insert(index, records[i]);
21407             records[i].join(this);
21408         }
21409         this.fireEvent("add", this, records, index);
21410     },
21411
21412     /**
21413      * Get the index within the cache of the passed Record.
21414      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21415      * @return {Number} The index of the passed Record. Returns -1 if not found.
21416      */
21417     indexOf : function(record){
21418         return this.data.indexOf(record);
21419     },
21420
21421     /**
21422      * Get the index within the cache of the Record with the passed id.
21423      * @param {String} id The id of the Record to find.
21424      * @return {Number} The index of the Record. Returns -1 if not found.
21425      */
21426     indexOfId : function(id){
21427         return this.data.indexOfKey(id);
21428     },
21429
21430     /**
21431      * Get the Record with the specified id.
21432      * @param {String} id The id of the Record to find.
21433      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21434      */
21435     getById : function(id){
21436         return this.data.key(id);
21437     },
21438
21439     /**
21440      * Get the Record at the specified index.
21441      * @param {Number} index The index of the Record to find.
21442      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21443      */
21444     getAt : function(index){
21445         return this.data.itemAt(index);
21446     },
21447
21448     /**
21449      * Returns a range of Records between specified indices.
21450      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21451      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21452      * @return {Roo.data.Record[]} An array of Records
21453      */
21454     getRange : function(start, end){
21455         return this.data.getRange(start, end);
21456     },
21457
21458     // private
21459     storeOptions : function(o){
21460         o = Roo.apply({}, o);
21461         delete o.callback;
21462         delete o.scope;
21463         this.lastOptions = o;
21464     },
21465
21466     /**
21467      * Loads the Record cache from the configured Proxy using the configured Reader.
21468      * <p>
21469      * If using remote paging, then the first load call must specify the <em>start</em>
21470      * and <em>limit</em> properties in the options.params property to establish the initial
21471      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21472      * <p>
21473      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21474      * and this call will return before the new data has been loaded. Perform any post-processing
21475      * in a callback function, or in a "load" event handler.</strong>
21476      * <p>
21477      * @param {Object} options An object containing properties which control loading options:<ul>
21478      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21479      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21480      * passed the following arguments:<ul>
21481      * <li>r : Roo.data.Record[]</li>
21482      * <li>options: Options object from the load call</li>
21483      * <li>success: Boolean success indicator</li></ul></li>
21484      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21485      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21486      * </ul>
21487      */
21488     load : function(options){
21489         options = options || {};
21490         if(this.fireEvent("beforeload", this, options) !== false){
21491             this.storeOptions(options);
21492             var p = Roo.apply(options.params || {}, this.baseParams);
21493             // if meta was not loaded from remote source.. try requesting it.
21494             if (!this.reader.metaFromRemote) {
21495                 p._requestMeta = 1;
21496             }
21497             if(this.sortInfo && this.remoteSort){
21498                 var pn = this.paramNames;
21499                 p[pn["sort"]] = this.sortInfo.field;
21500                 p[pn["dir"]] = this.sortInfo.direction;
21501             }
21502             if (this.multiSort) {
21503                 var pn = this.paramNames;
21504                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21505             }
21506             
21507             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21508         }
21509     },
21510
21511     /**
21512      * Reloads the Record cache from the configured Proxy using the configured Reader and
21513      * the options from the last load operation performed.
21514      * @param {Object} options (optional) An object containing properties which may override the options
21515      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21516      * the most recently used options are reused).
21517      */
21518     reload : function(options){
21519         this.load(Roo.applyIf(options||{}, this.lastOptions));
21520     },
21521
21522     // private
21523     // Called as a callback by the Reader during a load operation.
21524     loadRecords : function(o, options, success){
21525         if(!o || success === false){
21526             if(success !== false){
21527                 this.fireEvent("load", this, [], options, o);
21528             }
21529             if(options.callback){
21530                 options.callback.call(options.scope || this, [], options, false);
21531             }
21532             return;
21533         }
21534         // if data returned failure - throw an exception.
21535         if (o.success === false) {
21536             // show a message if no listener is registered.
21537             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21538                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21539             }
21540             // loadmask wil be hooked into this..
21541             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21542             return;
21543         }
21544         var r = o.records, t = o.totalRecords || r.length;
21545         
21546         this.fireEvent("beforeloadadd", this, r, options, o);
21547         
21548         if(!options || options.add !== true){
21549             if(this.pruneModifiedRecords){
21550                 this.modified = [];
21551             }
21552             for(var i = 0, len = r.length; i < len; i++){
21553                 r[i].join(this);
21554             }
21555             if(this.snapshot){
21556                 this.data = this.snapshot;
21557                 delete this.snapshot;
21558             }
21559             this.data.clear();
21560             this.data.addAll(r);
21561             this.totalLength = t;
21562             this.applySort();
21563             this.fireEvent("datachanged", this);
21564         }else{
21565             this.totalLength = Math.max(t, this.data.length+r.length);
21566             this.add(r);
21567         }
21568         this.fireEvent("load", this, r, options, o);
21569         if(options.callback){
21570             options.callback.call(options.scope || this, r, options, true);
21571         }
21572     },
21573
21574
21575     /**
21576      * Loads data from a passed data block. A Reader which understands the format of the data
21577      * must have been configured in the constructor.
21578      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21579      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21580      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21581      */
21582     loadData : function(o, append){
21583         var r = this.reader.readRecords(o);
21584         this.loadRecords(r, {add: append}, true);
21585     },
21586
21587     /**
21588      * Gets the number of cached records.
21589      * <p>
21590      * <em>If using paging, this may not be the total size of the dataset. If the data object
21591      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21592      * the data set size</em>
21593      */
21594     getCount : function(){
21595         return this.data.length || 0;
21596     },
21597
21598     /**
21599      * Gets the total number of records in the dataset as returned by the server.
21600      * <p>
21601      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21602      * the dataset size</em>
21603      */
21604     getTotalCount : function(){
21605         return this.totalLength || 0;
21606     },
21607
21608     /**
21609      * Returns the sort state of the Store as an object with two properties:
21610      * <pre><code>
21611  field {String} The name of the field by which the Records are sorted
21612  direction {String} The sort order, "ASC" or "DESC"
21613      * </code></pre>
21614      */
21615     getSortState : function(){
21616         return this.sortInfo;
21617     },
21618
21619     // private
21620     applySort : function(){
21621         if(this.sortInfo && !this.remoteSort){
21622             var s = this.sortInfo, f = s.field;
21623             var st = this.fields.get(f).sortType;
21624             var fn = function(r1, r2){
21625                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21626                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21627             };
21628             this.data.sort(s.direction, fn);
21629             if(this.snapshot && this.snapshot != this.data){
21630                 this.snapshot.sort(s.direction, fn);
21631             }
21632         }
21633     },
21634
21635     /**
21636      * Sets the default sort column and order to be used by the next load operation.
21637      * @param {String} fieldName The name of the field to sort by.
21638      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21639      */
21640     setDefaultSort : function(field, dir){
21641         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21642     },
21643
21644     /**
21645      * Sort the Records.
21646      * If remote sorting is used, the sort is performed on the server, and the cache is
21647      * reloaded. If local sorting is used, the cache is sorted internally.
21648      * @param {String} fieldName The name of the field to sort by.
21649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21650      */
21651     sort : function(fieldName, dir){
21652         var f = this.fields.get(fieldName);
21653         if(!dir){
21654             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21655             
21656             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21657                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21658             }else{
21659                 dir = f.sortDir;
21660             }
21661         }
21662         this.sortToggle[f.name] = dir;
21663         this.sortInfo = {field: f.name, direction: dir};
21664         if(!this.remoteSort){
21665             this.applySort();
21666             this.fireEvent("datachanged", this);
21667         }else{
21668             this.load(this.lastOptions);
21669         }
21670     },
21671
21672     /**
21673      * Calls the specified function for each of the Records in the cache.
21674      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21675      * Returning <em>false</em> aborts and exits the iteration.
21676      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21677      */
21678     each : function(fn, scope){
21679         this.data.each(fn, scope);
21680     },
21681
21682     /**
21683      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21684      * (e.g., during paging).
21685      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21686      */
21687     getModifiedRecords : function(){
21688         return this.modified;
21689     },
21690
21691     // private
21692     createFilterFn : function(property, value, anyMatch){
21693         if(!value.exec){ // not a regex
21694             value = String(value);
21695             if(value.length == 0){
21696                 return false;
21697             }
21698             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21699         }
21700         return function(r){
21701             return value.test(r.data[property]);
21702         };
21703     },
21704
21705     /**
21706      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21707      * @param {String} property A field on your records
21708      * @param {Number} start The record index to start at (defaults to 0)
21709      * @param {Number} end The last record index to include (defaults to length - 1)
21710      * @return {Number} The sum
21711      */
21712     sum : function(property, start, end){
21713         var rs = this.data.items, v = 0;
21714         start = start || 0;
21715         end = (end || end === 0) ? end : rs.length-1;
21716
21717         for(var i = start; i <= end; i++){
21718             v += (rs[i].data[property] || 0);
21719         }
21720         return v;
21721     },
21722
21723     /**
21724      * Filter the records by a specified property.
21725      * @param {String} field A field on your records
21726      * @param {String/RegExp} value Either a string that the field
21727      * should start with or a RegExp to test against the field
21728      * @param {Boolean} anyMatch True to match any part not just the beginning
21729      */
21730     filter : function(property, value, anyMatch){
21731         var fn = this.createFilterFn(property, value, anyMatch);
21732         return fn ? this.filterBy(fn) : this.clearFilter();
21733     },
21734
21735     /**
21736      * Filter by a function. The specified function will be called with each
21737      * record in this data source. If the function returns true the record is included,
21738      * otherwise it is filtered.
21739      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21740      * @param {Object} scope (optional) The scope of the function (defaults to this)
21741      */
21742     filterBy : function(fn, scope){
21743         this.snapshot = this.snapshot || this.data;
21744         this.data = this.queryBy(fn, scope||this);
21745         this.fireEvent("datachanged", this);
21746     },
21747
21748     /**
21749      * Query the records by a specified property.
21750      * @param {String} field A field on your records
21751      * @param {String/RegExp} value Either a string that the field
21752      * should start with or a RegExp to test against the field
21753      * @param {Boolean} anyMatch True to match any part not just the beginning
21754      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21755      */
21756     query : function(property, value, anyMatch){
21757         var fn = this.createFilterFn(property, value, anyMatch);
21758         return fn ? this.queryBy(fn) : this.data.clone();
21759     },
21760
21761     /**
21762      * Query by a function. The specified function will be called with each
21763      * record in this data source. If the function returns true the record is included
21764      * in the results.
21765      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21766      * @param {Object} scope (optional) The scope of the function (defaults to this)
21767       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21768      **/
21769     queryBy : function(fn, scope){
21770         var data = this.snapshot || this.data;
21771         return data.filterBy(fn, scope||this);
21772     },
21773
21774     /**
21775      * Collects unique values for a particular dataIndex from this store.
21776      * @param {String} dataIndex The property to collect
21777      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21778      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21779      * @return {Array} An array of the unique values
21780      **/
21781     collect : function(dataIndex, allowNull, bypassFilter){
21782         var d = (bypassFilter === true && this.snapshot) ?
21783                 this.snapshot.items : this.data.items;
21784         var v, sv, r = [], l = {};
21785         for(var i = 0, len = d.length; i < len; i++){
21786             v = d[i].data[dataIndex];
21787             sv = String(v);
21788             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21789                 l[sv] = true;
21790                 r[r.length] = v;
21791             }
21792         }
21793         return r;
21794     },
21795
21796     /**
21797      * Revert to a view of the Record cache with no filtering applied.
21798      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21799      */
21800     clearFilter : function(suppressEvent){
21801         if(this.snapshot && this.snapshot != this.data){
21802             this.data = this.snapshot;
21803             delete this.snapshot;
21804             if(suppressEvent !== true){
21805                 this.fireEvent("datachanged", this);
21806             }
21807         }
21808     },
21809
21810     // private
21811     afterEdit : function(record){
21812         if(this.modified.indexOf(record) == -1){
21813             this.modified.push(record);
21814         }
21815         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21816     },
21817     
21818     // private
21819     afterReject : function(record){
21820         this.modified.remove(record);
21821         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21822     },
21823
21824     // private
21825     afterCommit : function(record){
21826         this.modified.remove(record);
21827         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21828     },
21829
21830     /**
21831      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21832      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21833      */
21834     commitChanges : function(){
21835         var m = this.modified.slice(0);
21836         this.modified = [];
21837         for(var i = 0, len = m.length; i < len; i++){
21838             m[i].commit();
21839         }
21840     },
21841
21842     /**
21843      * Cancel outstanding changes on all changed records.
21844      */
21845     rejectChanges : function(){
21846         var m = this.modified.slice(0);
21847         this.modified = [];
21848         for(var i = 0, len = m.length; i < len; i++){
21849             m[i].reject();
21850         }
21851     },
21852
21853     onMetaChange : function(meta, rtype, o){
21854         this.recordType = rtype;
21855         this.fields = rtype.prototype.fields;
21856         delete this.snapshot;
21857         this.sortInfo = meta.sortInfo || this.sortInfo;
21858         this.modified = [];
21859         this.fireEvent('metachange', this, this.reader.meta);
21860     },
21861     
21862     moveIndex : function(data, type)
21863     {
21864         var index = this.indexOf(data);
21865         
21866         var newIndex = index + type;
21867         
21868         this.remove(data);
21869         
21870         this.insert(newIndex, data);
21871         
21872     }
21873 });/*
21874  * Based on:
21875  * Ext JS Library 1.1.1
21876  * Copyright(c) 2006-2007, Ext JS, LLC.
21877  *
21878  * Originally Released Under LGPL - original licence link has changed is not relivant.
21879  *
21880  * Fork - LGPL
21881  * <script type="text/javascript">
21882  */
21883
21884 /**
21885  * @class Roo.data.SimpleStore
21886  * @extends Roo.data.Store
21887  * Small helper class to make creating Stores from Array data easier.
21888  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21889  * @cfg {Array} fields An array of field definition objects, or field name strings.
21890  * @cfg {Array} data The multi-dimensional array of data
21891  * @constructor
21892  * @param {Object} config
21893  */
21894 Roo.data.SimpleStore = function(config){
21895     Roo.data.SimpleStore.superclass.constructor.call(this, {
21896         isLocal : true,
21897         reader: new Roo.data.ArrayReader({
21898                 id: config.id
21899             },
21900             Roo.data.Record.create(config.fields)
21901         ),
21902         proxy : new Roo.data.MemoryProxy(config.data)
21903     });
21904     this.load();
21905 };
21906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21907  * Based on:
21908  * Ext JS Library 1.1.1
21909  * Copyright(c) 2006-2007, Ext JS, LLC.
21910  *
21911  * Originally Released Under LGPL - original licence link has changed is not relivant.
21912  *
21913  * Fork - LGPL
21914  * <script type="text/javascript">
21915  */
21916
21917 /**
21918 /**
21919  * @extends Roo.data.Store
21920  * @class Roo.data.JsonStore
21921  * Small helper class to make creating Stores for JSON data easier. <br/>
21922 <pre><code>
21923 var store = new Roo.data.JsonStore({
21924     url: 'get-images.php',
21925     root: 'images',
21926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21927 });
21928 </code></pre>
21929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21930  * JsonReader and HttpProxy (unless inline data is provided).</b>
21931  * @cfg {Array} fields An array of field definition objects, or field name strings.
21932  * @constructor
21933  * @param {Object} config
21934  */
21935 Roo.data.JsonStore = function(c){
21936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21938         reader: new Roo.data.JsonReader(c, c.fields)
21939     }));
21940 };
21941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21942  * Based on:
21943  * Ext JS Library 1.1.1
21944  * Copyright(c) 2006-2007, Ext JS, LLC.
21945  *
21946  * Originally Released Under LGPL - original licence link has changed is not relivant.
21947  *
21948  * Fork - LGPL
21949  * <script type="text/javascript">
21950  */
21951
21952  
21953 Roo.data.Field = function(config){
21954     if(typeof config == "string"){
21955         config = {name: config};
21956     }
21957     Roo.apply(this, config);
21958     
21959     if(!this.type){
21960         this.type = "auto";
21961     }
21962     
21963     var st = Roo.data.SortTypes;
21964     // named sortTypes are supported, here we look them up
21965     if(typeof this.sortType == "string"){
21966         this.sortType = st[this.sortType];
21967     }
21968     
21969     // set default sortType for strings and dates
21970     if(!this.sortType){
21971         switch(this.type){
21972             case "string":
21973                 this.sortType = st.asUCString;
21974                 break;
21975             case "date":
21976                 this.sortType = st.asDate;
21977                 break;
21978             default:
21979                 this.sortType = st.none;
21980         }
21981     }
21982
21983     // define once
21984     var stripRe = /[\$,%]/g;
21985
21986     // prebuilt conversion function for this field, instead of
21987     // switching every time we're reading a value
21988     if(!this.convert){
21989         var cv, dateFormat = this.dateFormat;
21990         switch(this.type){
21991             case "":
21992             case "auto":
21993             case undefined:
21994                 cv = function(v){ return v; };
21995                 break;
21996             case "string":
21997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21998                 break;
21999             case "int":
22000                 cv = function(v){
22001                     return v !== undefined && v !== null && v !== '' ?
22002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22003                     };
22004                 break;
22005             case "float":
22006                 cv = function(v){
22007                     return v !== undefined && v !== null && v !== '' ?
22008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22009                     };
22010                 break;
22011             case "bool":
22012             case "boolean":
22013                 cv = function(v){ return v === true || v === "true" || v == 1; };
22014                 break;
22015             case "date":
22016                 cv = function(v){
22017                     if(!v){
22018                         return '';
22019                     }
22020                     if(v instanceof Date){
22021                         return v;
22022                     }
22023                     if(dateFormat){
22024                         if(dateFormat == "timestamp"){
22025                             return new Date(v*1000);
22026                         }
22027                         return Date.parseDate(v, dateFormat);
22028                     }
22029                     var parsed = Date.parse(v);
22030                     return parsed ? new Date(parsed) : null;
22031                 };
22032              break;
22033             
22034         }
22035         this.convert = cv;
22036     }
22037 };
22038
22039 Roo.data.Field.prototype = {
22040     dateFormat: null,
22041     defaultValue: "",
22042     mapping: null,
22043     sortType : null,
22044     sortDir : "ASC"
22045 };/*
22046  * Based on:
22047  * Ext JS Library 1.1.1
22048  * Copyright(c) 2006-2007, Ext JS, LLC.
22049  *
22050  * Originally Released Under LGPL - original licence link has changed is not relivant.
22051  *
22052  * Fork - LGPL
22053  * <script type="text/javascript">
22054  */
22055  
22056 // Base class for reading structured data from a data source.  This class is intended to be
22057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22058
22059 /**
22060  * @class Roo.data.DataReader
22061  * Base class for reading structured data from a data source.  This class is intended to be
22062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22063  */
22064
22065 Roo.data.DataReader = function(meta, recordType){
22066     
22067     this.meta = meta;
22068     
22069     this.recordType = recordType instanceof Array ? 
22070         Roo.data.Record.create(recordType) : recordType;
22071 };
22072
22073 Roo.data.DataReader.prototype = {
22074      /**
22075      * Create an empty record
22076      * @param {Object} data (optional) - overlay some values
22077      * @return {Roo.data.Record} record created.
22078      */
22079     newRow :  function(d) {
22080         var da =  {};
22081         this.recordType.prototype.fields.each(function(c) {
22082             switch( c.type) {
22083                 case 'int' : da[c.name] = 0; break;
22084                 case 'date' : da[c.name] = new Date(); break;
22085                 case 'float' : da[c.name] = 0.0; break;
22086                 case 'boolean' : da[c.name] = false; break;
22087                 default : da[c.name] = ""; break;
22088             }
22089             
22090         });
22091         return new this.recordType(Roo.apply(da, d));
22092     }
22093     
22094 };/*
22095  * Based on:
22096  * Ext JS Library 1.1.1
22097  * Copyright(c) 2006-2007, Ext JS, LLC.
22098  *
22099  * Originally Released Under LGPL - original licence link has changed is not relivant.
22100  *
22101  * Fork - LGPL
22102  * <script type="text/javascript">
22103  */
22104
22105 /**
22106  * @class Roo.data.DataProxy
22107  * @extends Roo.data.Observable
22108  * This class is an abstract base class for implementations which provide retrieval of
22109  * unformatted data objects.<br>
22110  * <p>
22111  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22112  * (of the appropriate type which knows how to parse the data object) to provide a block of
22113  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22114  * <p>
22115  * Custom implementations must implement the load method as described in
22116  * {@link Roo.data.HttpProxy#load}.
22117  */
22118 Roo.data.DataProxy = function(){
22119     this.addEvents({
22120         /**
22121          * @event beforeload
22122          * Fires before a network request is made to retrieve a data object.
22123          * @param {Object} This DataProxy object.
22124          * @param {Object} params The params parameter to the load function.
22125          */
22126         beforeload : true,
22127         /**
22128          * @event load
22129          * Fires before the load method's callback is called.
22130          * @param {Object} This DataProxy object.
22131          * @param {Object} o The data object.
22132          * @param {Object} arg The callback argument object passed to the load function.
22133          */
22134         load : true,
22135         /**
22136          * @event loadexception
22137          * Fires if an Exception occurs during data retrieval.
22138          * @param {Object} This DataProxy object.
22139          * @param {Object} o The data object.
22140          * @param {Object} arg The callback argument object passed to the load function.
22141          * @param {Object} e The Exception.
22142          */
22143         loadexception : true
22144     });
22145     Roo.data.DataProxy.superclass.constructor.call(this);
22146 };
22147
22148 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22149
22150     /**
22151      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22152      */
22153 /*
22154  * Based on:
22155  * Ext JS Library 1.1.1
22156  * Copyright(c) 2006-2007, Ext JS, LLC.
22157  *
22158  * Originally Released Under LGPL - original licence link has changed is not relivant.
22159  *
22160  * Fork - LGPL
22161  * <script type="text/javascript">
22162  */
22163 /**
22164  * @class Roo.data.MemoryProxy
22165  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22166  * to the Reader when its load method is called.
22167  * @constructor
22168  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22169  */
22170 Roo.data.MemoryProxy = function(data){
22171     if (data.data) {
22172         data = data.data;
22173     }
22174     Roo.data.MemoryProxy.superclass.constructor.call(this);
22175     this.data = data;
22176 };
22177
22178 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22179     /**
22180      * Load data from the requested source (in this case an in-memory
22181      * data object passed to the constructor), read the data object into
22182      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22183      * process that block using the passed callback.
22184      * @param {Object} params This parameter is not used by the MemoryProxy class.
22185      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22186      * object into a block of Roo.data.Records.
22187      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22188      * The function must be passed <ul>
22189      * <li>The Record block object</li>
22190      * <li>The "arg" argument from the load function</li>
22191      * <li>A boolean success indicator</li>
22192      * </ul>
22193      * @param {Object} scope The scope in which to call the callback
22194      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22195      */
22196     load : function(params, reader, callback, scope, arg){
22197         params = params || {};
22198         var result;
22199         try {
22200             result = reader.readRecords(this.data);
22201         }catch(e){
22202             this.fireEvent("loadexception", this, arg, null, e);
22203             callback.call(scope, null, arg, false);
22204             return;
22205         }
22206         callback.call(scope, result, arg, true);
22207     },
22208     
22209     // private
22210     update : function(params, records){
22211         
22212     }
22213 });/*
22214  * Based on:
22215  * Ext JS Library 1.1.1
22216  * Copyright(c) 2006-2007, Ext JS, LLC.
22217  *
22218  * Originally Released Under LGPL - original licence link has changed is not relivant.
22219  *
22220  * Fork - LGPL
22221  * <script type="text/javascript">
22222  */
22223 /**
22224  * @class Roo.data.HttpProxy
22225  * @extends Roo.data.DataProxy
22226  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22227  * configured to reference a certain URL.<br><br>
22228  * <p>
22229  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22230  * from which the running page was served.<br><br>
22231  * <p>
22232  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22233  * <p>
22234  * Be aware that to enable the browser to parse an XML document, the server must set
22235  * the Content-Type header in the HTTP response to "text/xml".
22236  * @constructor
22237  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22238  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22239  * will be used to make the request.
22240  */
22241 Roo.data.HttpProxy = function(conn){
22242     Roo.data.HttpProxy.superclass.constructor.call(this);
22243     // is conn a conn config or a real conn?
22244     this.conn = conn;
22245     this.useAjax = !conn || !conn.events;
22246   
22247 };
22248
22249 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22250     // thse are take from connection...
22251     
22252     /**
22253      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22254      */
22255     /**
22256      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22257      * extra parameters to each request made by this object. (defaults to undefined)
22258      */
22259     /**
22260      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22261      *  to each request made by this object. (defaults to undefined)
22262      */
22263     /**
22264      * @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)
22265      */
22266     /**
22267      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22268      */
22269      /**
22270      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22271      * @type Boolean
22272      */
22273   
22274
22275     /**
22276      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22277      * @type Boolean
22278      */
22279     /**
22280      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22281      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22282      * a finer-grained basis than the DataProxy events.
22283      */
22284     getConnection : function(){
22285         return this.useAjax ? Roo.Ajax : this.conn;
22286     },
22287
22288     /**
22289      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22290      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22291      * process that block using the passed callback.
22292      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22293      * for the request to the remote server.
22294      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22295      * object into a block of Roo.data.Records.
22296      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22297      * The function must be passed <ul>
22298      * <li>The Record block object</li>
22299      * <li>The "arg" argument from the load function</li>
22300      * <li>A boolean success indicator</li>
22301      * </ul>
22302      * @param {Object} scope The scope in which to call the callback
22303      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22304      */
22305     load : function(params, reader, callback, scope, arg){
22306         if(this.fireEvent("beforeload", this, params) !== false){
22307             var  o = {
22308                 params : params || {},
22309                 request: {
22310                     callback : callback,
22311                     scope : scope,
22312                     arg : arg
22313                 },
22314                 reader: reader,
22315                 callback : this.loadResponse,
22316                 scope: this
22317             };
22318             if(this.useAjax){
22319                 Roo.applyIf(o, this.conn);
22320                 if(this.activeRequest){
22321                     Roo.Ajax.abort(this.activeRequest);
22322                 }
22323                 this.activeRequest = Roo.Ajax.request(o);
22324             }else{
22325                 this.conn.request(o);
22326             }
22327         }else{
22328             callback.call(scope||this, null, arg, false);
22329         }
22330     },
22331
22332     // private
22333     loadResponse : function(o, success, response){
22334         delete this.activeRequest;
22335         if(!success){
22336             this.fireEvent("loadexception", this, o, response);
22337             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22338             return;
22339         }
22340         var result;
22341         try {
22342             result = o.reader.read(response);
22343         }catch(e){
22344             this.fireEvent("loadexception", this, o, response, e);
22345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22346             return;
22347         }
22348         
22349         this.fireEvent("load", this, o, o.request.arg);
22350         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22351     },
22352
22353     // private
22354     update : function(dataSet){
22355
22356     },
22357
22358     // private
22359     updateResponse : function(dataSet){
22360
22361     }
22362 });/*
22363  * Based on:
22364  * Ext JS Library 1.1.1
22365  * Copyright(c) 2006-2007, Ext JS, LLC.
22366  *
22367  * Originally Released Under LGPL - original licence link has changed is not relivant.
22368  *
22369  * Fork - LGPL
22370  * <script type="text/javascript">
22371  */
22372
22373 /**
22374  * @class Roo.data.ScriptTagProxy
22375  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22376  * other than the originating domain of the running page.<br><br>
22377  * <p>
22378  * <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
22379  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22380  * <p>
22381  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22382  * source code that is used as the source inside a &lt;script> tag.<br><br>
22383  * <p>
22384  * In order for the browser to process the returned data, the server must wrap the data object
22385  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22386  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22387  * depending on whether the callback name was passed:
22388  * <p>
22389  * <pre><code>
22390 boolean scriptTag = false;
22391 String cb = request.getParameter("callback");
22392 if (cb != null) {
22393     scriptTag = true;
22394     response.setContentType("text/javascript");
22395 } else {
22396     response.setContentType("application/x-json");
22397 }
22398 Writer out = response.getWriter();
22399 if (scriptTag) {
22400     out.write(cb + "(");
22401 }
22402 out.print(dataBlock.toJsonString());
22403 if (scriptTag) {
22404     out.write(");");
22405 }
22406 </pre></code>
22407  *
22408  * @constructor
22409  * @param {Object} config A configuration object.
22410  */
22411 Roo.data.ScriptTagProxy = function(config){
22412     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22413     Roo.apply(this, config);
22414     this.head = document.getElementsByTagName("head")[0];
22415 };
22416
22417 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22418
22419 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22420     /**
22421      * @cfg {String} url The URL from which to request the data object.
22422      */
22423     /**
22424      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22425      */
22426     timeout : 30000,
22427     /**
22428      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22429      * the server the name of the callback function set up by the load call to process the returned data object.
22430      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22431      * javascript output which calls this named function passing the data object as its only parameter.
22432      */
22433     callbackParam : "callback",
22434     /**
22435      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22436      * name to the request.
22437      */
22438     nocache : true,
22439
22440     /**
22441      * Load data from the configured URL, read the data object into
22442      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22443      * process that block using the passed callback.
22444      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22445      * for the request to the remote server.
22446      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22447      * object into a block of Roo.data.Records.
22448      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22449      * The function must be passed <ul>
22450      * <li>The Record block object</li>
22451      * <li>The "arg" argument from the load function</li>
22452      * <li>A boolean success indicator</li>
22453      * </ul>
22454      * @param {Object} scope The scope in which to call the callback
22455      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22456      */
22457     load : function(params, reader, callback, scope, arg){
22458         if(this.fireEvent("beforeload", this, params) !== false){
22459
22460             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22461
22462             var url = this.url;
22463             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22464             if(this.nocache){
22465                 url += "&_dc=" + (new Date().getTime());
22466             }
22467             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22468             var trans = {
22469                 id : transId,
22470                 cb : "stcCallback"+transId,
22471                 scriptId : "stcScript"+transId,
22472                 params : params,
22473                 arg : arg,
22474                 url : url,
22475                 callback : callback,
22476                 scope : scope,
22477                 reader : reader
22478             };
22479             var conn = this;
22480
22481             window[trans.cb] = function(o){
22482                 conn.handleResponse(o, trans);
22483             };
22484
22485             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22486
22487             if(this.autoAbort !== false){
22488                 this.abort();
22489             }
22490
22491             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22492
22493             var script = document.createElement("script");
22494             script.setAttribute("src", url);
22495             script.setAttribute("type", "text/javascript");
22496             script.setAttribute("id", trans.scriptId);
22497             this.head.appendChild(script);
22498
22499             this.trans = trans;
22500         }else{
22501             callback.call(scope||this, null, arg, false);
22502         }
22503     },
22504
22505     // private
22506     isLoading : function(){
22507         return this.trans ? true : false;
22508     },
22509
22510     /**
22511      * Abort the current server request.
22512      */
22513     abort : function(){
22514         if(this.isLoading()){
22515             this.destroyTrans(this.trans);
22516         }
22517     },
22518
22519     // private
22520     destroyTrans : function(trans, isLoaded){
22521         this.head.removeChild(document.getElementById(trans.scriptId));
22522         clearTimeout(trans.timeoutId);
22523         if(isLoaded){
22524             window[trans.cb] = undefined;
22525             try{
22526                 delete window[trans.cb];
22527             }catch(e){}
22528         }else{
22529             // if hasn't been loaded, wait for load to remove it to prevent script error
22530             window[trans.cb] = function(){
22531                 window[trans.cb] = undefined;
22532                 try{
22533                     delete window[trans.cb];
22534                 }catch(e){}
22535             };
22536         }
22537     },
22538
22539     // private
22540     handleResponse : function(o, trans){
22541         this.trans = false;
22542         this.destroyTrans(trans, true);
22543         var result;
22544         try {
22545             result = trans.reader.readRecords(o);
22546         }catch(e){
22547             this.fireEvent("loadexception", this, o, trans.arg, e);
22548             trans.callback.call(trans.scope||window, null, trans.arg, false);
22549             return;
22550         }
22551         this.fireEvent("load", this, o, trans.arg);
22552         trans.callback.call(trans.scope||window, result, trans.arg, true);
22553     },
22554
22555     // private
22556     handleFailure : function(trans){
22557         this.trans = false;
22558         this.destroyTrans(trans, false);
22559         this.fireEvent("loadexception", this, null, trans.arg);
22560         trans.callback.call(trans.scope||window, null, trans.arg, false);
22561     }
22562 });/*
22563  * Based on:
22564  * Ext JS Library 1.1.1
22565  * Copyright(c) 2006-2007, Ext JS, LLC.
22566  *
22567  * Originally Released Under LGPL - original licence link has changed is not relivant.
22568  *
22569  * Fork - LGPL
22570  * <script type="text/javascript">
22571  */
22572
22573 /**
22574  * @class Roo.data.JsonReader
22575  * @extends Roo.data.DataReader
22576  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22577  * based on mappings in a provided Roo.data.Record constructor.
22578  * 
22579  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22580  * in the reply previously. 
22581  * 
22582  * <p>
22583  * Example code:
22584  * <pre><code>
22585 var RecordDef = Roo.data.Record.create([
22586     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22587     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22588 ]);
22589 var myReader = new Roo.data.JsonReader({
22590     totalProperty: "results",    // The property which contains the total dataset size (optional)
22591     root: "rows",                // The property which contains an Array of row objects
22592     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22593 }, RecordDef);
22594 </code></pre>
22595  * <p>
22596  * This would consume a JSON file like this:
22597  * <pre><code>
22598 { 'results': 2, 'rows': [
22599     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22600     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22601 }
22602 </code></pre>
22603  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22604  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22605  * paged from the remote server.
22606  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22607  * @cfg {String} root name of the property which contains the Array of row objects.
22608  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22609  * @constructor
22610  * Create a new JsonReader
22611  * @param {Object} meta Metadata configuration options
22612  * @param {Object} recordType Either an Array of field definition objects,
22613  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22614  */
22615 Roo.data.JsonReader = function(meta, recordType){
22616     
22617     meta = meta || {};
22618     // set some defaults:
22619     Roo.applyIf(meta, {
22620         totalProperty: 'total',
22621         successProperty : 'success',
22622         root : 'data',
22623         id : 'id'
22624     });
22625     
22626     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22627 };
22628 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22629     
22630     /**
22631      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22632      * Used by Store query builder to append _requestMeta to params.
22633      * 
22634      */
22635     metaFromRemote : false,
22636     /**
22637      * This method is only used by a DataProxy which has retrieved data from a remote server.
22638      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22639      * @return {Object} data A data block which is used by an Roo.data.Store object as
22640      * a cache of Roo.data.Records.
22641      */
22642     read : function(response){
22643         var json = response.responseText;
22644        
22645         var o = /* eval:var:o */ eval("("+json+")");
22646         if(!o) {
22647             throw {message: "JsonReader.read: Json object not found"};
22648         }
22649         
22650         if(o.metaData){
22651             
22652             delete this.ef;
22653             this.metaFromRemote = true;
22654             this.meta = o.metaData;
22655             this.recordType = Roo.data.Record.create(o.metaData.fields);
22656             this.onMetaChange(this.meta, this.recordType, o);
22657         }
22658         return this.readRecords(o);
22659     },
22660
22661     // private function a store will implement
22662     onMetaChange : function(meta, recordType, o){
22663
22664     },
22665
22666     /**
22667          * @ignore
22668          */
22669     simpleAccess: function(obj, subsc) {
22670         return obj[subsc];
22671     },
22672
22673         /**
22674          * @ignore
22675          */
22676     getJsonAccessor: function(){
22677         var re = /[\[\.]/;
22678         return function(expr) {
22679             try {
22680                 return(re.test(expr))
22681                     ? new Function("obj", "return obj." + expr)
22682                     : function(obj){
22683                         return obj[expr];
22684                     };
22685             } catch(e){}
22686             return Roo.emptyFn;
22687         };
22688     }(),
22689
22690     /**
22691      * Create a data block containing Roo.data.Records from an XML document.
22692      * @param {Object} o An object which contains an Array of row objects in the property specified
22693      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22694      * which contains the total size of the dataset.
22695      * @return {Object} data A data block which is used by an Roo.data.Store object as
22696      * a cache of Roo.data.Records.
22697      */
22698     readRecords : function(o){
22699         /**
22700          * After any data loads, the raw JSON data is available for further custom processing.
22701          * @type Object
22702          */
22703         this.o = o;
22704         var s = this.meta, Record = this.recordType,
22705             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22706
22707 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22708         if (!this.ef) {
22709             if(s.totalProperty) {
22710                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22711                 }
22712                 if(s.successProperty) {
22713                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22714                 }
22715                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22716                 if (s.id) {
22717                         var g = this.getJsonAccessor(s.id);
22718                         this.getId = function(rec) {
22719                                 var r = g(rec);  
22720                                 return (r === undefined || r === "") ? null : r;
22721                         };
22722                 } else {
22723                         this.getId = function(){return null;};
22724                 }
22725             this.ef = [];
22726             for(var jj = 0; jj < fl; jj++){
22727                 f = fi[jj];
22728                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22729                 this.ef[jj] = this.getJsonAccessor(map);
22730             }
22731         }
22732
22733         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22734         if(s.totalProperty){
22735             var vt = parseInt(this.getTotal(o), 10);
22736             if(!isNaN(vt)){
22737                 totalRecords = vt;
22738             }
22739         }
22740         if(s.successProperty){
22741             var vs = this.getSuccess(o);
22742             if(vs === false || vs === 'false'){
22743                 success = false;
22744             }
22745         }
22746         var records = [];
22747         for(var i = 0; i < c; i++){
22748                 var n = root[i];
22749             var values = {};
22750             var id = this.getId(n);
22751             for(var j = 0; j < fl; j++){
22752                 f = fi[j];
22753             var v = this.ef[j](n);
22754             if (!f.convert) {
22755                 Roo.log('missing convert for ' + f.name);
22756                 Roo.log(f);
22757                 continue;
22758             }
22759             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22760             }
22761             var record = new Record(values, id);
22762             record.json = n;
22763             records[i] = record;
22764         }
22765         return {
22766             raw : o,
22767             success : success,
22768             records : records,
22769             totalRecords : totalRecords
22770         };
22771     }
22772 });/*
22773  * Based on:
22774  * Ext JS Library 1.1.1
22775  * Copyright(c) 2006-2007, Ext JS, LLC.
22776  *
22777  * Originally Released Under LGPL - original licence link has changed is not relivant.
22778  *
22779  * Fork - LGPL
22780  * <script type="text/javascript">
22781  */
22782
22783 /**
22784  * @class Roo.data.XmlReader
22785  * @extends Roo.data.DataReader
22786  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22787  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22788  * <p>
22789  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22790  * header in the HTTP response must be set to "text/xml".</em>
22791  * <p>
22792  * Example code:
22793  * <pre><code>
22794 var RecordDef = Roo.data.Record.create([
22795    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22796    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22797 ]);
22798 var myReader = new Roo.data.XmlReader({
22799    totalRecords: "results", // The element which contains the total dataset size (optional)
22800    record: "row",           // The repeated element which contains row information
22801    id: "id"                 // The element within the row that provides an ID for the record (optional)
22802 }, RecordDef);
22803 </code></pre>
22804  * <p>
22805  * This would consume an XML file like this:
22806  * <pre><code>
22807 &lt;?xml?>
22808 &lt;dataset>
22809  &lt;results>2&lt;/results>
22810  &lt;row>
22811    &lt;id>1&lt;/id>
22812    &lt;name>Bill&lt;/name>
22813    &lt;occupation>Gardener&lt;/occupation>
22814  &lt;/row>
22815  &lt;row>
22816    &lt;id>2&lt;/id>
22817    &lt;name>Ben&lt;/name>
22818    &lt;occupation>Horticulturalist&lt;/occupation>
22819  &lt;/row>
22820 &lt;/dataset>
22821 </code></pre>
22822  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22823  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22824  * paged from the remote server.
22825  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22826  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22827  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22828  * a record identifier value.
22829  * @constructor
22830  * Create a new XmlReader
22831  * @param {Object} meta Metadata configuration options
22832  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22833  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22834  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22835  */
22836 Roo.data.XmlReader = function(meta, recordType){
22837     meta = meta || {};
22838     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22839 };
22840 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22841     /**
22842      * This method is only used by a DataProxy which has retrieved data from a remote server.
22843          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22844          * to contain a method called 'responseXML' that returns an XML document object.
22845      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22846      * a cache of Roo.data.Records.
22847      */
22848     read : function(response){
22849         var doc = response.responseXML;
22850         if(!doc) {
22851             throw {message: "XmlReader.read: XML Document not available"};
22852         }
22853         return this.readRecords(doc);
22854     },
22855
22856     /**
22857      * Create a data block containing Roo.data.Records from an XML document.
22858          * @param {Object} doc A parsed XML document.
22859      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22860      * a cache of Roo.data.Records.
22861      */
22862     readRecords : function(doc){
22863         /**
22864          * After any data loads/reads, the raw XML Document is available for further custom processing.
22865          * @type XMLDocument
22866          */
22867         this.xmlData = doc;
22868         var root = doc.documentElement || doc;
22869         var q = Roo.DomQuery;
22870         var recordType = this.recordType, fields = recordType.prototype.fields;
22871         var sid = this.meta.id;
22872         var totalRecords = 0, success = true;
22873         if(this.meta.totalRecords){
22874             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22875         }
22876         
22877         if(this.meta.success){
22878             var sv = q.selectValue(this.meta.success, root, true);
22879             success = sv !== false && sv !== 'false';
22880         }
22881         var records = [];
22882         var ns = q.select(this.meta.record, root);
22883         for(var i = 0, len = ns.length; i < len; i++) {
22884                 var n = ns[i];
22885                 var values = {};
22886                 var id = sid ? q.selectValue(sid, n) : undefined;
22887                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22888                     var f = fields.items[j];
22889                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22890                     v = f.convert(v);
22891                     values[f.name] = v;
22892                 }
22893                 var record = new recordType(values, id);
22894                 record.node = n;
22895                 records[records.length] = record;
22896             }
22897
22898             return {
22899                 success : success,
22900                 records : records,
22901                 totalRecords : totalRecords || records.length
22902             };
22903     }
22904 });/*
22905  * Based on:
22906  * Ext JS Library 1.1.1
22907  * Copyright(c) 2006-2007, Ext JS, LLC.
22908  *
22909  * Originally Released Under LGPL - original licence link has changed is not relivant.
22910  *
22911  * Fork - LGPL
22912  * <script type="text/javascript">
22913  */
22914
22915 /**
22916  * @class Roo.data.ArrayReader
22917  * @extends Roo.data.DataReader
22918  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22919  * Each element of that Array represents a row of data fields. The
22920  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22921  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22922  * <p>
22923  * Example code:.
22924  * <pre><code>
22925 var RecordDef = Roo.data.Record.create([
22926     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22927     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22928 ]);
22929 var myReader = new Roo.data.ArrayReader({
22930     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22931 }, RecordDef);
22932 </code></pre>
22933  * <p>
22934  * This would consume an Array like this:
22935  * <pre><code>
22936 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22937   </code></pre>
22938  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22939  * @constructor
22940  * Create a new JsonReader
22941  * @param {Object} meta Metadata configuration options.
22942  * @param {Object} recordType Either an Array of field definition objects
22943  * as specified to {@link Roo.data.Record#create},
22944  * or an {@link Roo.data.Record} object
22945  * created using {@link Roo.data.Record#create}.
22946  */
22947 Roo.data.ArrayReader = function(meta, recordType){
22948     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22949 };
22950
22951 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22952     /**
22953      * Create a data block containing Roo.data.Records from an XML document.
22954      * @param {Object} o An Array of row objects which represents the dataset.
22955      * @return {Object} data A data block which is used by an Roo.data.Store object as
22956      * a cache of Roo.data.Records.
22957      */
22958     readRecords : function(o){
22959         var sid = this.meta ? this.meta.id : null;
22960         var recordType = this.recordType, fields = recordType.prototype.fields;
22961         var records = [];
22962         var root = o;
22963             for(var i = 0; i < root.length; i++){
22964                     var n = root[i];
22965                 var values = {};
22966                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22967                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22968                 var f = fields.items[j];
22969                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22970                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22971                 v = f.convert(v);
22972                 values[f.name] = v;
22973             }
22974                 var record = new recordType(values, id);
22975                 record.json = n;
22976                 records[records.length] = record;
22977             }
22978             return {
22979                 records : records,
22980                 totalRecords : records.length
22981             };
22982     }
22983 });/*
22984  * Based on:
22985  * Ext JS Library 1.1.1
22986  * Copyright(c) 2006-2007, Ext JS, LLC.
22987  *
22988  * Originally Released Under LGPL - original licence link has changed is not relivant.
22989  *
22990  * Fork - LGPL
22991  * <script type="text/javascript">
22992  */
22993
22994
22995 /**
22996  * @class Roo.data.Tree
22997  * @extends Roo.util.Observable
22998  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22999  * in the tree have most standard DOM functionality.
23000  * @constructor
23001  * @param {Node} root (optional) The root node
23002  */
23003 Roo.data.Tree = function(root){
23004    this.nodeHash = {};
23005    /**
23006     * The root node for this tree
23007     * @type Node
23008     */
23009    this.root = null;
23010    if(root){
23011        this.setRootNode(root);
23012    }
23013    this.addEvents({
23014        /**
23015         * @event append
23016         * Fires when a new child node is appended to a node in this tree.
23017         * @param {Tree} tree The owner tree
23018         * @param {Node} parent The parent node
23019         * @param {Node} node The newly appended node
23020         * @param {Number} index The index of the newly appended node
23021         */
23022        "append" : true,
23023        /**
23024         * @event remove
23025         * Fires when a child node is removed from a node in this tree.
23026         * @param {Tree} tree The owner tree
23027         * @param {Node} parent The parent node
23028         * @param {Node} node The child node removed
23029         */
23030        "remove" : true,
23031        /**
23032         * @event move
23033         * Fires when a node is moved to a new location in the tree
23034         * @param {Tree} tree The owner tree
23035         * @param {Node} node The node moved
23036         * @param {Node} oldParent The old parent of this node
23037         * @param {Node} newParent The new parent of this node
23038         * @param {Number} index The index it was moved to
23039         */
23040        "move" : true,
23041        /**
23042         * @event insert
23043         * Fires when a new child node is inserted in a node in this tree.
23044         * @param {Tree} tree The owner tree
23045         * @param {Node} parent The parent node
23046         * @param {Node} node The child node inserted
23047         * @param {Node} refNode The child node the node was inserted before
23048         */
23049        "insert" : true,
23050        /**
23051         * @event beforeappend
23052         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23053         * @param {Tree} tree The owner tree
23054         * @param {Node} parent The parent node
23055         * @param {Node} node The child node to be appended
23056         */
23057        "beforeappend" : true,
23058        /**
23059         * @event beforeremove
23060         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23061         * @param {Tree} tree The owner tree
23062         * @param {Node} parent The parent node
23063         * @param {Node} node The child node to be removed
23064         */
23065        "beforeremove" : true,
23066        /**
23067         * @event beforemove
23068         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23069         * @param {Tree} tree The owner tree
23070         * @param {Node} node The node being moved
23071         * @param {Node} oldParent The parent of the node
23072         * @param {Node} newParent The new parent the node is moving to
23073         * @param {Number} index The index it is being moved to
23074         */
23075        "beforemove" : true,
23076        /**
23077         * @event beforeinsert
23078         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23079         * @param {Tree} tree The owner tree
23080         * @param {Node} parent The parent node
23081         * @param {Node} node The child node to be inserted
23082         * @param {Node} refNode The child node the node is being inserted before
23083         */
23084        "beforeinsert" : true
23085    });
23086
23087     Roo.data.Tree.superclass.constructor.call(this);
23088 };
23089
23090 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23091     pathSeparator: "/",
23092
23093     proxyNodeEvent : function(){
23094         return this.fireEvent.apply(this, arguments);
23095     },
23096
23097     /**
23098      * Returns the root node for this tree.
23099      * @return {Node}
23100      */
23101     getRootNode : function(){
23102         return this.root;
23103     },
23104
23105     /**
23106      * Sets the root node for this tree.
23107      * @param {Node} node
23108      * @return {Node}
23109      */
23110     setRootNode : function(node){
23111         this.root = node;
23112         node.ownerTree = this;
23113         node.isRoot = true;
23114         this.registerNode(node);
23115         return node;
23116     },
23117
23118     /**
23119      * Gets a node in this tree by its id.
23120      * @param {String} id
23121      * @return {Node}
23122      */
23123     getNodeById : function(id){
23124         return this.nodeHash[id];
23125     },
23126
23127     registerNode : function(node){
23128         this.nodeHash[node.id] = node;
23129     },
23130
23131     unregisterNode : function(node){
23132         delete this.nodeHash[node.id];
23133     },
23134
23135     toString : function(){
23136         return "[Tree"+(this.id?" "+this.id:"")+"]";
23137     }
23138 });
23139
23140 /**
23141  * @class Roo.data.Node
23142  * @extends Roo.util.Observable
23143  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23144  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23145  * @constructor
23146  * @param {Object} attributes The attributes/config for the node
23147  */
23148 Roo.data.Node = function(attributes){
23149     /**
23150      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23151      * @type {Object}
23152      */
23153     this.attributes = attributes || {};
23154     this.leaf = this.attributes.leaf;
23155     /**
23156      * The node id. @type String
23157      */
23158     this.id = this.attributes.id;
23159     if(!this.id){
23160         this.id = Roo.id(null, "ynode-");
23161         this.attributes.id = this.id;
23162     }
23163      
23164     
23165     /**
23166      * All child nodes of this node. @type Array
23167      */
23168     this.childNodes = [];
23169     if(!this.childNodes.indexOf){ // indexOf is a must
23170         this.childNodes.indexOf = function(o){
23171             for(var i = 0, len = this.length; i < len; i++){
23172                 if(this[i] == o) {
23173                     return i;
23174                 }
23175             }
23176             return -1;
23177         };
23178     }
23179     /**
23180      * The parent node for this node. @type Node
23181      */
23182     this.parentNode = null;
23183     /**
23184      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23185      */
23186     this.firstChild = null;
23187     /**
23188      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23189      */
23190     this.lastChild = null;
23191     /**
23192      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23193      */
23194     this.previousSibling = null;
23195     /**
23196      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23197      */
23198     this.nextSibling = null;
23199
23200     this.addEvents({
23201        /**
23202         * @event append
23203         * Fires when a new child node is appended
23204         * @param {Tree} tree The owner tree
23205         * @param {Node} this This node
23206         * @param {Node} node The newly appended node
23207         * @param {Number} index The index of the newly appended node
23208         */
23209        "append" : true,
23210        /**
23211         * @event remove
23212         * Fires when a child node is removed
23213         * @param {Tree} tree The owner tree
23214         * @param {Node} this This node
23215         * @param {Node} node The removed node
23216         */
23217        "remove" : true,
23218        /**
23219         * @event move
23220         * Fires when this node is moved to a new location in the tree
23221         * @param {Tree} tree The owner tree
23222         * @param {Node} this This node
23223         * @param {Node} oldParent The old parent of this node
23224         * @param {Node} newParent The new parent of this node
23225         * @param {Number} index The index it was moved to
23226         */
23227        "move" : true,
23228        /**
23229         * @event insert
23230         * Fires when a new child node is inserted.
23231         * @param {Tree} tree The owner tree
23232         * @param {Node} this This node
23233         * @param {Node} node The child node inserted
23234         * @param {Node} refNode The child node the node was inserted before
23235         */
23236        "insert" : true,
23237        /**
23238         * @event beforeappend
23239         * Fires before a new child is appended, return false to cancel the append.
23240         * @param {Tree} tree The owner tree
23241         * @param {Node} this This node
23242         * @param {Node} node The child node to be appended
23243         */
23244        "beforeappend" : true,
23245        /**
23246         * @event beforeremove
23247         * Fires before a child is removed, return false to cancel the remove.
23248         * @param {Tree} tree The owner tree
23249         * @param {Node} this This node
23250         * @param {Node} node The child node to be removed
23251         */
23252        "beforeremove" : true,
23253        /**
23254         * @event beforemove
23255         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23256         * @param {Tree} tree The owner tree
23257         * @param {Node} this This node
23258         * @param {Node} oldParent The parent of this node
23259         * @param {Node} newParent The new parent this node is moving to
23260         * @param {Number} index The index it is being moved to
23261         */
23262        "beforemove" : true,
23263        /**
23264         * @event beforeinsert
23265         * Fires before a new child is inserted, return false to cancel the insert.
23266         * @param {Tree} tree The owner tree
23267         * @param {Node} this This node
23268         * @param {Node} node The child node to be inserted
23269         * @param {Node} refNode The child node the node is being inserted before
23270         */
23271        "beforeinsert" : true
23272    });
23273     this.listeners = this.attributes.listeners;
23274     Roo.data.Node.superclass.constructor.call(this);
23275 };
23276
23277 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23278     fireEvent : function(evtName){
23279         // first do standard event for this node
23280         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23281             return false;
23282         }
23283         // then bubble it up to the tree if the event wasn't cancelled
23284         var ot = this.getOwnerTree();
23285         if(ot){
23286             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23287                 return false;
23288             }
23289         }
23290         return true;
23291     },
23292
23293     /**
23294      * Returns true if this node is a leaf
23295      * @return {Boolean}
23296      */
23297     isLeaf : function(){
23298         return this.leaf === true;
23299     },
23300
23301     // private
23302     setFirstChild : function(node){
23303         this.firstChild = node;
23304     },
23305
23306     //private
23307     setLastChild : function(node){
23308         this.lastChild = node;
23309     },
23310
23311
23312     /**
23313      * Returns true if this node is the last child of its parent
23314      * @return {Boolean}
23315      */
23316     isLast : function(){
23317        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23318     },
23319
23320     /**
23321      * Returns true if this node is the first child of its parent
23322      * @return {Boolean}
23323      */
23324     isFirst : function(){
23325        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23326     },
23327
23328     hasChildNodes : function(){
23329         return !this.isLeaf() && this.childNodes.length > 0;
23330     },
23331
23332     /**
23333      * Insert node(s) as the last child node of this node.
23334      * @param {Node/Array} node The node or Array of nodes to append
23335      * @return {Node} The appended node if single append, or null if an array was passed
23336      */
23337     appendChild : function(node){
23338         var multi = false;
23339         if(node instanceof Array){
23340             multi = node;
23341         }else if(arguments.length > 1){
23342             multi = arguments;
23343         }
23344         // if passed an array or multiple args do them one by one
23345         if(multi){
23346             for(var i = 0, len = multi.length; i < len; i++) {
23347                 this.appendChild(multi[i]);
23348             }
23349         }else{
23350             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23351                 return false;
23352             }
23353             var index = this.childNodes.length;
23354             var oldParent = node.parentNode;
23355             // it's a move, make sure we move it cleanly
23356             if(oldParent){
23357                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23358                     return false;
23359                 }
23360                 oldParent.removeChild(node);
23361             }
23362             index = this.childNodes.length;
23363             if(index == 0){
23364                 this.setFirstChild(node);
23365             }
23366             this.childNodes.push(node);
23367             node.parentNode = this;
23368             var ps = this.childNodes[index-1];
23369             if(ps){
23370                 node.previousSibling = ps;
23371                 ps.nextSibling = node;
23372             }else{
23373                 node.previousSibling = null;
23374             }
23375             node.nextSibling = null;
23376             this.setLastChild(node);
23377             node.setOwnerTree(this.getOwnerTree());
23378             this.fireEvent("append", this.ownerTree, this, node, index);
23379             if(oldParent){
23380                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23381             }
23382             return node;
23383         }
23384     },
23385
23386     /**
23387      * Removes a child node from this node.
23388      * @param {Node} node The node to remove
23389      * @return {Node} The removed node
23390      */
23391     removeChild : function(node){
23392         var index = this.childNodes.indexOf(node);
23393         if(index == -1){
23394             return false;
23395         }
23396         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23397             return false;
23398         }
23399
23400         // remove it from childNodes collection
23401         this.childNodes.splice(index, 1);
23402
23403         // update siblings
23404         if(node.previousSibling){
23405             node.previousSibling.nextSibling = node.nextSibling;
23406         }
23407         if(node.nextSibling){
23408             node.nextSibling.previousSibling = node.previousSibling;
23409         }
23410
23411         // update child refs
23412         if(this.firstChild == node){
23413             this.setFirstChild(node.nextSibling);
23414         }
23415         if(this.lastChild == node){
23416             this.setLastChild(node.previousSibling);
23417         }
23418
23419         node.setOwnerTree(null);
23420         // clear any references from the node
23421         node.parentNode = null;
23422         node.previousSibling = null;
23423         node.nextSibling = null;
23424         this.fireEvent("remove", this.ownerTree, this, node);
23425         return node;
23426     },
23427
23428     /**
23429      * Inserts the first node before the second node in this nodes childNodes collection.
23430      * @param {Node} node The node to insert
23431      * @param {Node} refNode The node to insert before (if null the node is appended)
23432      * @return {Node} The inserted node
23433      */
23434     insertBefore : function(node, refNode){
23435         if(!refNode){ // like standard Dom, refNode can be null for append
23436             return this.appendChild(node);
23437         }
23438         // nothing to do
23439         if(node == refNode){
23440             return false;
23441         }
23442
23443         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23444             return false;
23445         }
23446         var index = this.childNodes.indexOf(refNode);
23447         var oldParent = node.parentNode;
23448         var refIndex = index;
23449
23450         // when moving internally, indexes will change after remove
23451         if(oldParent == this && this.childNodes.indexOf(node) < index){
23452             refIndex--;
23453         }
23454
23455         // it's a move, make sure we move it cleanly
23456         if(oldParent){
23457             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23458                 return false;
23459             }
23460             oldParent.removeChild(node);
23461         }
23462         if(refIndex == 0){
23463             this.setFirstChild(node);
23464         }
23465         this.childNodes.splice(refIndex, 0, node);
23466         node.parentNode = this;
23467         var ps = this.childNodes[refIndex-1];
23468         if(ps){
23469             node.previousSibling = ps;
23470             ps.nextSibling = node;
23471         }else{
23472             node.previousSibling = null;
23473         }
23474         node.nextSibling = refNode;
23475         refNode.previousSibling = node;
23476         node.setOwnerTree(this.getOwnerTree());
23477         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23478         if(oldParent){
23479             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23480         }
23481         return node;
23482     },
23483
23484     /**
23485      * Returns the child node at the specified index.
23486      * @param {Number} index
23487      * @return {Node}
23488      */
23489     item : function(index){
23490         return this.childNodes[index];
23491     },
23492
23493     /**
23494      * Replaces one child node in this node with another.
23495      * @param {Node} newChild The replacement node
23496      * @param {Node} oldChild The node to replace
23497      * @return {Node} The replaced node
23498      */
23499     replaceChild : function(newChild, oldChild){
23500         this.insertBefore(newChild, oldChild);
23501         this.removeChild(oldChild);
23502         return oldChild;
23503     },
23504
23505     /**
23506      * Returns the index of a child node
23507      * @param {Node} node
23508      * @return {Number} The index of the node or -1 if it was not found
23509      */
23510     indexOf : function(child){
23511         return this.childNodes.indexOf(child);
23512     },
23513
23514     /**
23515      * Returns the tree this node is in.
23516      * @return {Tree}
23517      */
23518     getOwnerTree : function(){
23519         // if it doesn't have one, look for one
23520         if(!this.ownerTree){
23521             var p = this;
23522             while(p){
23523                 if(p.ownerTree){
23524                     this.ownerTree = p.ownerTree;
23525                     break;
23526                 }
23527                 p = p.parentNode;
23528             }
23529         }
23530         return this.ownerTree;
23531     },
23532
23533     /**
23534      * Returns depth of this node (the root node has a depth of 0)
23535      * @return {Number}
23536      */
23537     getDepth : function(){
23538         var depth = 0;
23539         var p = this;
23540         while(p.parentNode){
23541             ++depth;
23542             p = p.parentNode;
23543         }
23544         return depth;
23545     },
23546
23547     // private
23548     setOwnerTree : function(tree){
23549         // if it's move, we need to update everyone
23550         if(tree != this.ownerTree){
23551             if(this.ownerTree){
23552                 this.ownerTree.unregisterNode(this);
23553             }
23554             this.ownerTree = tree;
23555             var cs = this.childNodes;
23556             for(var i = 0, len = cs.length; i < len; i++) {
23557                 cs[i].setOwnerTree(tree);
23558             }
23559             if(tree){
23560                 tree.registerNode(this);
23561             }
23562         }
23563     },
23564
23565     /**
23566      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23567      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23568      * @return {String} The path
23569      */
23570     getPath : function(attr){
23571         attr = attr || "id";
23572         var p = this.parentNode;
23573         var b = [this.attributes[attr]];
23574         while(p){
23575             b.unshift(p.attributes[attr]);
23576             p = p.parentNode;
23577         }
23578         var sep = this.getOwnerTree().pathSeparator;
23579         return sep + b.join(sep);
23580     },
23581
23582     /**
23583      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23584      * function call will be the scope provided or the current node. The arguments to the function
23585      * will be the args provided or the current node. If the function returns false at any point,
23586      * the bubble is stopped.
23587      * @param {Function} fn The function to call
23588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23590      */
23591     bubble : function(fn, scope, args){
23592         var p = this;
23593         while(p){
23594             if(fn.call(scope || p, args || p) === false){
23595                 break;
23596             }
23597             p = p.parentNode;
23598         }
23599     },
23600
23601     /**
23602      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23603      * function call will be the scope provided or the current node. The arguments to the function
23604      * will be the args provided or the current node. If the function returns false at any point,
23605      * the cascade is stopped on that branch.
23606      * @param {Function} fn The function to call
23607      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23608      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23609      */
23610     cascade : function(fn, scope, args){
23611         if(fn.call(scope || this, args || this) !== false){
23612             var cs = this.childNodes;
23613             for(var i = 0, len = cs.length; i < len; i++) {
23614                 cs[i].cascade(fn, scope, args);
23615             }
23616         }
23617     },
23618
23619     /**
23620      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23621      * function call will be the scope provided or the current node. The arguments to the function
23622      * will be the args provided or the current node. If the function returns false at any point,
23623      * the iteration stops.
23624      * @param {Function} fn The function to call
23625      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23626      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23627      */
23628     eachChild : function(fn, scope, args){
23629         var cs = this.childNodes;
23630         for(var i = 0, len = cs.length; i < len; i++) {
23631                 if(fn.call(scope || this, args || cs[i]) === false){
23632                     break;
23633                 }
23634         }
23635     },
23636
23637     /**
23638      * Finds the first child that has the attribute with the specified value.
23639      * @param {String} attribute The attribute name
23640      * @param {Mixed} value The value to search for
23641      * @return {Node} The found child or null if none was found
23642      */
23643     findChild : function(attribute, value){
23644         var cs = this.childNodes;
23645         for(var i = 0, len = cs.length; i < len; i++) {
23646                 if(cs[i].attributes[attribute] == value){
23647                     return cs[i];
23648                 }
23649         }
23650         return null;
23651     },
23652
23653     /**
23654      * Finds the first child by a custom function. The child matches if the function passed
23655      * returns true.
23656      * @param {Function} fn
23657      * @param {Object} scope (optional)
23658      * @return {Node} The found child or null if none was found
23659      */
23660     findChildBy : function(fn, scope){
23661         var cs = this.childNodes;
23662         for(var i = 0, len = cs.length; i < len; i++) {
23663                 if(fn.call(scope||cs[i], cs[i]) === true){
23664                     return cs[i];
23665                 }
23666         }
23667         return null;
23668     },
23669
23670     /**
23671      * Sorts this nodes children using the supplied sort function
23672      * @param {Function} fn
23673      * @param {Object} scope (optional)
23674      */
23675     sort : function(fn, scope){
23676         var cs = this.childNodes;
23677         var len = cs.length;
23678         if(len > 0){
23679             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23680             cs.sort(sortFn);
23681             for(var i = 0; i < len; i++){
23682                 var n = cs[i];
23683                 n.previousSibling = cs[i-1];
23684                 n.nextSibling = cs[i+1];
23685                 if(i == 0){
23686                     this.setFirstChild(n);
23687                 }
23688                 if(i == len-1){
23689                     this.setLastChild(n);
23690                 }
23691             }
23692         }
23693     },
23694
23695     /**
23696      * Returns true if this node is an ancestor (at any point) of the passed node.
23697      * @param {Node} node
23698      * @return {Boolean}
23699      */
23700     contains : function(node){
23701         return node.isAncestor(this);
23702     },
23703
23704     /**
23705      * Returns true if the passed node is an ancestor (at any point) of this node.
23706      * @param {Node} node
23707      * @return {Boolean}
23708      */
23709     isAncestor : function(node){
23710         var p = this.parentNode;
23711         while(p){
23712             if(p == node){
23713                 return true;
23714             }
23715             p = p.parentNode;
23716         }
23717         return false;
23718     },
23719
23720     toString : function(){
23721         return "[Node"+(this.id?" "+this.id:"")+"]";
23722     }
23723 });/*
23724  * Based on:
23725  * Ext JS Library 1.1.1
23726  * Copyright(c) 2006-2007, Ext JS, LLC.
23727  *
23728  * Originally Released Under LGPL - original licence link has changed is not relivant.
23729  *
23730  * Fork - LGPL
23731  * <script type="text/javascript">
23732  */
23733  (function(){ 
23734 /**
23735  * @class Roo.Layer
23736  * @extends Roo.Element
23737  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23738  * automatic maintaining of shadow/shim positions.
23739  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23740  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23741  * you can pass a string with a CSS class name. False turns off the shadow.
23742  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23743  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23744  * @cfg {String} cls CSS class to add to the element
23745  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23746  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23747  * @constructor
23748  * @param {Object} config An object with config options.
23749  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23750  */
23751
23752 Roo.Layer = function(config, existingEl){
23753     config = config || {};
23754     var dh = Roo.DomHelper;
23755     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23756     if(existingEl){
23757         this.dom = Roo.getDom(existingEl);
23758     }
23759     if(!this.dom){
23760         var o = config.dh || {tag: "div", cls: "x-layer"};
23761         this.dom = dh.append(pel, o);
23762     }
23763     if(config.cls){
23764         this.addClass(config.cls);
23765     }
23766     this.constrain = config.constrain !== false;
23767     this.visibilityMode = Roo.Element.VISIBILITY;
23768     if(config.id){
23769         this.id = this.dom.id = config.id;
23770     }else{
23771         this.id = Roo.id(this.dom);
23772     }
23773     this.zindex = config.zindex || this.getZIndex();
23774     this.position("absolute", this.zindex);
23775     if(config.shadow){
23776         this.shadowOffset = config.shadowOffset || 4;
23777         this.shadow = new Roo.Shadow({
23778             offset : this.shadowOffset,
23779             mode : config.shadow
23780         });
23781     }else{
23782         this.shadowOffset = 0;
23783     }
23784     this.useShim = config.shim !== false && Roo.useShims;
23785     this.useDisplay = config.useDisplay;
23786     this.hide();
23787 };
23788
23789 var supr = Roo.Element.prototype;
23790
23791 // shims are shared among layer to keep from having 100 iframes
23792 var shims = [];
23793
23794 Roo.extend(Roo.Layer, Roo.Element, {
23795
23796     getZIndex : function(){
23797         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23798     },
23799
23800     getShim : function(){
23801         if(!this.useShim){
23802             return null;
23803         }
23804         if(this.shim){
23805             return this.shim;
23806         }
23807         var shim = shims.shift();
23808         if(!shim){
23809             shim = this.createShim();
23810             shim.enableDisplayMode('block');
23811             shim.dom.style.display = 'none';
23812             shim.dom.style.visibility = 'visible';
23813         }
23814         var pn = this.dom.parentNode;
23815         if(shim.dom.parentNode != pn){
23816             pn.insertBefore(shim.dom, this.dom);
23817         }
23818         shim.setStyle('z-index', this.getZIndex()-2);
23819         this.shim = shim;
23820         return shim;
23821     },
23822
23823     hideShim : function(){
23824         if(this.shim){
23825             this.shim.setDisplayed(false);
23826             shims.push(this.shim);
23827             delete this.shim;
23828         }
23829     },
23830
23831     disableShadow : function(){
23832         if(this.shadow){
23833             this.shadowDisabled = true;
23834             this.shadow.hide();
23835             this.lastShadowOffset = this.shadowOffset;
23836             this.shadowOffset = 0;
23837         }
23838     },
23839
23840     enableShadow : function(show){
23841         if(this.shadow){
23842             this.shadowDisabled = false;
23843             this.shadowOffset = this.lastShadowOffset;
23844             delete this.lastShadowOffset;
23845             if(show){
23846                 this.sync(true);
23847             }
23848         }
23849     },
23850
23851     // private
23852     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23853     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23854     sync : function(doShow){
23855         var sw = this.shadow;
23856         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23857             var sh = this.getShim();
23858
23859             var w = this.getWidth(),
23860                 h = this.getHeight();
23861
23862             var l = this.getLeft(true),
23863                 t = this.getTop(true);
23864
23865             if(sw && !this.shadowDisabled){
23866                 if(doShow && !sw.isVisible()){
23867                     sw.show(this);
23868                 }else{
23869                     sw.realign(l, t, w, h);
23870                 }
23871                 if(sh){
23872                     if(doShow){
23873                        sh.show();
23874                     }
23875                     // fit the shim behind the shadow, so it is shimmed too
23876                     var a = sw.adjusts, s = sh.dom.style;
23877                     s.left = (Math.min(l, l+a.l))+"px";
23878                     s.top = (Math.min(t, t+a.t))+"px";
23879                     s.width = (w+a.w)+"px";
23880                     s.height = (h+a.h)+"px";
23881                 }
23882             }else if(sh){
23883                 if(doShow){
23884                    sh.show();
23885                 }
23886                 sh.setSize(w, h);
23887                 sh.setLeftTop(l, t);
23888             }
23889             
23890         }
23891     },
23892
23893     // private
23894     destroy : function(){
23895         this.hideShim();
23896         if(this.shadow){
23897             this.shadow.hide();
23898         }
23899         this.removeAllListeners();
23900         var pn = this.dom.parentNode;
23901         if(pn){
23902             pn.removeChild(this.dom);
23903         }
23904         Roo.Element.uncache(this.id);
23905     },
23906
23907     remove : function(){
23908         this.destroy();
23909     },
23910
23911     // private
23912     beginUpdate : function(){
23913         this.updating = true;
23914     },
23915
23916     // private
23917     endUpdate : function(){
23918         this.updating = false;
23919         this.sync(true);
23920     },
23921
23922     // private
23923     hideUnders : function(negOffset){
23924         if(this.shadow){
23925             this.shadow.hide();
23926         }
23927         this.hideShim();
23928     },
23929
23930     // private
23931     constrainXY : function(){
23932         if(this.constrain){
23933             var vw = Roo.lib.Dom.getViewWidth(),
23934                 vh = Roo.lib.Dom.getViewHeight();
23935             var s = Roo.get(document).getScroll();
23936
23937             var xy = this.getXY();
23938             var x = xy[0], y = xy[1];   
23939             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23940             // only move it if it needs it
23941             var moved = false;
23942             // first validate right/bottom
23943             if((x + w) > vw+s.left){
23944                 x = vw - w - this.shadowOffset;
23945                 moved = true;
23946             }
23947             if((y + h) > vh+s.top){
23948                 y = vh - h - this.shadowOffset;
23949                 moved = true;
23950             }
23951             // then make sure top/left isn't negative
23952             if(x < s.left){
23953                 x = s.left;
23954                 moved = true;
23955             }
23956             if(y < s.top){
23957                 y = s.top;
23958                 moved = true;
23959             }
23960             if(moved){
23961                 if(this.avoidY){
23962                     var ay = this.avoidY;
23963                     if(y <= ay && (y+h) >= ay){
23964                         y = ay-h-5;   
23965                     }
23966                 }
23967                 xy = [x, y];
23968                 this.storeXY(xy);
23969                 supr.setXY.call(this, xy);
23970                 this.sync();
23971             }
23972         }
23973     },
23974
23975     isVisible : function(){
23976         return this.visible;    
23977     },
23978
23979     // private
23980     showAction : function(){
23981         this.visible = true; // track visibility to prevent getStyle calls
23982         if(this.useDisplay === true){
23983             this.setDisplayed("");
23984         }else if(this.lastXY){
23985             supr.setXY.call(this, this.lastXY);
23986         }else if(this.lastLT){
23987             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23988         }
23989     },
23990
23991     // private
23992     hideAction : function(){
23993         this.visible = false;
23994         if(this.useDisplay === true){
23995             this.setDisplayed(false);
23996         }else{
23997             this.setLeftTop(-10000,-10000);
23998         }
23999     },
24000
24001     // overridden Element method
24002     setVisible : function(v, a, d, c, e){
24003         if(v){
24004             this.showAction();
24005         }
24006         if(a && v){
24007             var cb = function(){
24008                 this.sync(true);
24009                 if(c){
24010                     c();
24011                 }
24012             }.createDelegate(this);
24013             supr.setVisible.call(this, true, true, d, cb, e);
24014         }else{
24015             if(!v){
24016                 this.hideUnders(true);
24017             }
24018             var cb = c;
24019             if(a){
24020                 cb = function(){
24021                     this.hideAction();
24022                     if(c){
24023                         c();
24024                     }
24025                 }.createDelegate(this);
24026             }
24027             supr.setVisible.call(this, v, a, d, cb, e);
24028             if(v){
24029                 this.sync(true);
24030             }else if(!a){
24031                 this.hideAction();
24032             }
24033         }
24034     },
24035
24036     storeXY : function(xy){
24037         delete this.lastLT;
24038         this.lastXY = xy;
24039     },
24040
24041     storeLeftTop : function(left, top){
24042         delete this.lastXY;
24043         this.lastLT = [left, top];
24044     },
24045
24046     // private
24047     beforeFx : function(){
24048         this.beforeAction();
24049         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24050     },
24051
24052     // private
24053     afterFx : function(){
24054         Roo.Layer.superclass.afterFx.apply(this, arguments);
24055         this.sync(this.isVisible());
24056     },
24057
24058     // private
24059     beforeAction : function(){
24060         if(!this.updating && this.shadow){
24061             this.shadow.hide();
24062         }
24063     },
24064
24065     // overridden Element method
24066     setLeft : function(left){
24067         this.storeLeftTop(left, this.getTop(true));
24068         supr.setLeft.apply(this, arguments);
24069         this.sync();
24070     },
24071
24072     setTop : function(top){
24073         this.storeLeftTop(this.getLeft(true), top);
24074         supr.setTop.apply(this, arguments);
24075         this.sync();
24076     },
24077
24078     setLeftTop : function(left, top){
24079         this.storeLeftTop(left, top);
24080         supr.setLeftTop.apply(this, arguments);
24081         this.sync();
24082     },
24083
24084     setXY : function(xy, a, d, c, e){
24085         this.fixDisplay();
24086         this.beforeAction();
24087         this.storeXY(xy);
24088         var cb = this.createCB(c);
24089         supr.setXY.call(this, xy, a, d, cb, e);
24090         if(!a){
24091             cb();
24092         }
24093     },
24094
24095     // private
24096     createCB : function(c){
24097         var el = this;
24098         return function(){
24099             el.constrainXY();
24100             el.sync(true);
24101             if(c){
24102                 c();
24103             }
24104         };
24105     },
24106
24107     // overridden Element method
24108     setX : function(x, a, d, c, e){
24109         this.setXY([x, this.getY()], a, d, c, e);
24110     },
24111
24112     // overridden Element method
24113     setY : function(y, a, d, c, e){
24114         this.setXY([this.getX(), y], a, d, c, e);
24115     },
24116
24117     // overridden Element method
24118     setSize : function(w, h, a, d, c, e){
24119         this.beforeAction();
24120         var cb = this.createCB(c);
24121         supr.setSize.call(this, w, h, a, d, cb, e);
24122         if(!a){
24123             cb();
24124         }
24125     },
24126
24127     // overridden Element method
24128     setWidth : function(w, a, d, c, e){
24129         this.beforeAction();
24130         var cb = this.createCB(c);
24131         supr.setWidth.call(this, w, a, d, cb, e);
24132         if(!a){
24133             cb();
24134         }
24135     },
24136
24137     // overridden Element method
24138     setHeight : function(h, a, d, c, e){
24139         this.beforeAction();
24140         var cb = this.createCB(c);
24141         supr.setHeight.call(this, h, a, d, cb, e);
24142         if(!a){
24143             cb();
24144         }
24145     },
24146
24147     // overridden Element method
24148     setBounds : function(x, y, w, h, a, d, c, e){
24149         this.beforeAction();
24150         var cb = this.createCB(c);
24151         if(!a){
24152             this.storeXY([x, y]);
24153             supr.setXY.call(this, [x, y]);
24154             supr.setSize.call(this, w, h, a, d, cb, e);
24155             cb();
24156         }else{
24157             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24158         }
24159         return this;
24160     },
24161     
24162     /**
24163      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24164      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24165      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24166      * @param {Number} zindex The new z-index to set
24167      * @return {this} The Layer
24168      */
24169     setZIndex : function(zindex){
24170         this.zindex = zindex;
24171         this.setStyle("z-index", zindex + 2);
24172         if(this.shadow){
24173             this.shadow.setZIndex(zindex + 1);
24174         }
24175         if(this.shim){
24176             this.shim.setStyle("z-index", zindex);
24177         }
24178     }
24179 });
24180 })();/*
24181  * Based on:
24182  * Ext JS Library 1.1.1
24183  * Copyright(c) 2006-2007, Ext JS, LLC.
24184  *
24185  * Originally Released Under LGPL - original licence link has changed is not relivant.
24186  *
24187  * Fork - LGPL
24188  * <script type="text/javascript">
24189  */
24190
24191
24192 /**
24193  * @class Roo.Shadow
24194  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24195  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24196  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24197  * @constructor
24198  * Create a new Shadow
24199  * @param {Object} config The config object
24200  */
24201 Roo.Shadow = function(config){
24202     Roo.apply(this, config);
24203     if(typeof this.mode != "string"){
24204         this.mode = this.defaultMode;
24205     }
24206     var o = this.offset, a = {h: 0};
24207     var rad = Math.floor(this.offset/2);
24208     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24209         case "drop":
24210             a.w = 0;
24211             a.l = a.t = o;
24212             a.t -= 1;
24213             if(Roo.isIE){
24214                 a.l -= this.offset + rad;
24215                 a.t -= this.offset + rad;
24216                 a.w -= rad;
24217                 a.h -= rad;
24218                 a.t += 1;
24219             }
24220         break;
24221         case "sides":
24222             a.w = (o*2);
24223             a.l = -o;
24224             a.t = o-1;
24225             if(Roo.isIE){
24226                 a.l -= (this.offset - rad);
24227                 a.t -= this.offset + rad;
24228                 a.l += 1;
24229                 a.w -= (this.offset - rad)*2;
24230                 a.w -= rad + 1;
24231                 a.h -= 1;
24232             }
24233         break;
24234         case "frame":
24235             a.w = a.h = (o*2);
24236             a.l = a.t = -o;
24237             a.t += 1;
24238             a.h -= 2;
24239             if(Roo.isIE){
24240                 a.l -= (this.offset - rad);
24241                 a.t -= (this.offset - rad);
24242                 a.l += 1;
24243                 a.w -= (this.offset + rad + 1);
24244                 a.h -= (this.offset + rad);
24245                 a.h += 1;
24246             }
24247         break;
24248     };
24249
24250     this.adjusts = a;
24251 };
24252
24253 Roo.Shadow.prototype = {
24254     /**
24255      * @cfg {String} mode
24256      * The shadow display mode.  Supports the following options:<br />
24257      * sides: Shadow displays on both sides and bottom only<br />
24258      * frame: Shadow displays equally on all four sides<br />
24259      * drop: Traditional bottom-right drop shadow (default)
24260      */
24261     /**
24262      * @cfg {String} offset
24263      * The number of pixels to offset the shadow from the element (defaults to 4)
24264      */
24265     offset: 4,
24266
24267     // private
24268     defaultMode: "drop",
24269
24270     /**
24271      * Displays the shadow under the target element
24272      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24273      */
24274     show : function(target){
24275         target = Roo.get(target);
24276         if(!this.el){
24277             this.el = Roo.Shadow.Pool.pull();
24278             if(this.el.dom.nextSibling != target.dom){
24279                 this.el.insertBefore(target);
24280             }
24281         }
24282         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24283         if(Roo.isIE){
24284             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24285         }
24286         this.realign(
24287             target.getLeft(true),
24288             target.getTop(true),
24289             target.getWidth(),
24290             target.getHeight()
24291         );
24292         this.el.dom.style.display = "block";
24293     },
24294
24295     /**
24296      * Returns true if the shadow is visible, else false
24297      */
24298     isVisible : function(){
24299         return this.el ? true : false;  
24300     },
24301
24302     /**
24303      * Direct alignment when values are already available. Show must be called at least once before
24304      * calling this method to ensure it is initialized.
24305      * @param {Number} left The target element left position
24306      * @param {Number} top The target element top position
24307      * @param {Number} width The target element width
24308      * @param {Number} height The target element height
24309      */
24310     realign : function(l, t, w, h){
24311         if(!this.el){
24312             return;
24313         }
24314         var a = this.adjusts, d = this.el.dom, s = d.style;
24315         var iea = 0;
24316         s.left = (l+a.l)+"px";
24317         s.top = (t+a.t)+"px";
24318         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24319  
24320         if(s.width != sws || s.height != shs){
24321             s.width = sws;
24322             s.height = shs;
24323             if(!Roo.isIE){
24324                 var cn = d.childNodes;
24325                 var sww = Math.max(0, (sw-12))+"px";
24326                 cn[0].childNodes[1].style.width = sww;
24327                 cn[1].childNodes[1].style.width = sww;
24328                 cn[2].childNodes[1].style.width = sww;
24329                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24330             }
24331         }
24332     },
24333
24334     /**
24335      * Hides this shadow
24336      */
24337     hide : function(){
24338         if(this.el){
24339             this.el.dom.style.display = "none";
24340             Roo.Shadow.Pool.push(this.el);
24341             delete this.el;
24342         }
24343     },
24344
24345     /**
24346      * Adjust the z-index of this shadow
24347      * @param {Number} zindex The new z-index
24348      */
24349     setZIndex : function(z){
24350         this.zIndex = z;
24351         if(this.el){
24352             this.el.setStyle("z-index", z);
24353         }
24354     }
24355 };
24356
24357 // Private utility class that manages the internal Shadow cache
24358 Roo.Shadow.Pool = function(){
24359     var p = [];
24360     var markup = Roo.isIE ?
24361                  '<div class="x-ie-shadow"></div>' :
24362                  '<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>';
24363     return {
24364         pull : function(){
24365             var sh = p.shift();
24366             if(!sh){
24367                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24368                 sh.autoBoxAdjust = false;
24369             }
24370             return sh;
24371         },
24372
24373         push : function(sh){
24374             p.push(sh);
24375         }
24376     };
24377 }();/*
24378  * Based on:
24379  * Ext JS Library 1.1.1
24380  * Copyright(c) 2006-2007, Ext JS, LLC.
24381  *
24382  * Originally Released Under LGPL - original licence link has changed is not relivant.
24383  *
24384  * Fork - LGPL
24385  * <script type="text/javascript">
24386  */
24387
24388
24389 /**
24390  * @class Roo.SplitBar
24391  * @extends Roo.util.Observable
24392  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24393  * <br><br>
24394  * Usage:
24395  * <pre><code>
24396 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24397                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24398 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24399 split.minSize = 100;
24400 split.maxSize = 600;
24401 split.animate = true;
24402 split.on('moved', splitterMoved);
24403 </code></pre>
24404  * @constructor
24405  * Create a new SplitBar
24406  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24407  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24408  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24409  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24410                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24411                         position of the SplitBar).
24412  */
24413 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24414     
24415     /** @private */
24416     this.el = Roo.get(dragElement, true);
24417     this.el.dom.unselectable = "on";
24418     /** @private */
24419     this.resizingEl = Roo.get(resizingElement, true);
24420
24421     /**
24422      * @private
24423      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24424      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24425      * @type Number
24426      */
24427     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24428     
24429     /**
24430      * The minimum size of the resizing element. (Defaults to 0)
24431      * @type Number
24432      */
24433     this.minSize = 0;
24434     
24435     /**
24436      * The maximum size of the resizing element. (Defaults to 2000)
24437      * @type Number
24438      */
24439     this.maxSize = 2000;
24440     
24441     /**
24442      * Whether to animate the transition to the new size
24443      * @type Boolean
24444      */
24445     this.animate = false;
24446     
24447     /**
24448      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24449      * @type Boolean
24450      */
24451     this.useShim = false;
24452     
24453     /** @private */
24454     this.shim = null;
24455     
24456     if(!existingProxy){
24457         /** @private */
24458         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24459     }else{
24460         this.proxy = Roo.get(existingProxy).dom;
24461     }
24462     /** @private */
24463     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24464     
24465     /** @private */
24466     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24467     
24468     /** @private */
24469     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24470     
24471     /** @private */
24472     this.dragSpecs = {};
24473     
24474     /**
24475      * @private The adapter to use to positon and resize elements
24476      */
24477     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24478     this.adapter.init(this);
24479     
24480     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24481         /** @private */
24482         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24483         this.el.addClass("x-splitbar-h");
24484     }else{
24485         /** @private */
24486         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24487         this.el.addClass("x-splitbar-v");
24488     }
24489     
24490     this.addEvents({
24491         /**
24492          * @event resize
24493          * Fires when the splitter is moved (alias for {@link #event-moved})
24494          * @param {Roo.SplitBar} this
24495          * @param {Number} newSize the new width or height
24496          */
24497         "resize" : true,
24498         /**
24499          * @event moved
24500          * Fires when the splitter is moved
24501          * @param {Roo.SplitBar} this
24502          * @param {Number} newSize the new width or height
24503          */
24504         "moved" : true,
24505         /**
24506          * @event beforeresize
24507          * Fires before the splitter is dragged
24508          * @param {Roo.SplitBar} this
24509          */
24510         "beforeresize" : true,
24511
24512         "beforeapply" : true
24513     });
24514
24515     Roo.util.Observable.call(this);
24516 };
24517
24518 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24519     onStartProxyDrag : function(x, y){
24520         this.fireEvent("beforeresize", this);
24521         if(!this.overlay){
24522             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24523             o.unselectable();
24524             o.enableDisplayMode("block");
24525             // all splitbars share the same overlay
24526             Roo.SplitBar.prototype.overlay = o;
24527         }
24528         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24529         this.overlay.show();
24530         Roo.get(this.proxy).setDisplayed("block");
24531         var size = this.adapter.getElementSize(this);
24532         this.activeMinSize = this.getMinimumSize();;
24533         this.activeMaxSize = this.getMaximumSize();;
24534         var c1 = size - this.activeMinSize;
24535         var c2 = Math.max(this.activeMaxSize - size, 0);
24536         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24537             this.dd.resetConstraints();
24538             this.dd.setXConstraint(
24539                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24540                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24541             );
24542             this.dd.setYConstraint(0, 0);
24543         }else{
24544             this.dd.resetConstraints();
24545             this.dd.setXConstraint(0, 0);
24546             this.dd.setYConstraint(
24547                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24548                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24549             );
24550          }
24551         this.dragSpecs.startSize = size;
24552         this.dragSpecs.startPoint = [x, y];
24553         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24554     },
24555     
24556     /** 
24557      * @private Called after the drag operation by the DDProxy
24558      */
24559     onEndProxyDrag : function(e){
24560         Roo.get(this.proxy).setDisplayed(false);
24561         var endPoint = Roo.lib.Event.getXY(e);
24562         if(this.overlay){
24563             this.overlay.hide();
24564         }
24565         var newSize;
24566         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24567             newSize = this.dragSpecs.startSize + 
24568                 (this.placement == Roo.SplitBar.LEFT ?
24569                     endPoint[0] - this.dragSpecs.startPoint[0] :
24570                     this.dragSpecs.startPoint[0] - endPoint[0]
24571                 );
24572         }else{
24573             newSize = this.dragSpecs.startSize + 
24574                 (this.placement == Roo.SplitBar.TOP ?
24575                     endPoint[1] - this.dragSpecs.startPoint[1] :
24576                     this.dragSpecs.startPoint[1] - endPoint[1]
24577                 );
24578         }
24579         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24580         if(newSize != this.dragSpecs.startSize){
24581             if(this.fireEvent('beforeapply', this, newSize) !== false){
24582                 this.adapter.setElementSize(this, newSize);
24583                 this.fireEvent("moved", this, newSize);
24584                 this.fireEvent("resize", this, newSize);
24585             }
24586         }
24587     },
24588     
24589     /**
24590      * Get the adapter this SplitBar uses
24591      * @return The adapter object
24592      */
24593     getAdapter : function(){
24594         return this.adapter;
24595     },
24596     
24597     /**
24598      * Set the adapter this SplitBar uses
24599      * @param {Object} adapter A SplitBar adapter object
24600      */
24601     setAdapter : function(adapter){
24602         this.adapter = adapter;
24603         this.adapter.init(this);
24604     },
24605     
24606     /**
24607      * Gets the minimum size for the resizing element
24608      * @return {Number} The minimum size
24609      */
24610     getMinimumSize : function(){
24611         return this.minSize;
24612     },
24613     
24614     /**
24615      * Sets the minimum size for the resizing element
24616      * @param {Number} minSize The minimum size
24617      */
24618     setMinimumSize : function(minSize){
24619         this.minSize = minSize;
24620     },
24621     
24622     /**
24623      * Gets the maximum size for the resizing element
24624      * @return {Number} The maximum size
24625      */
24626     getMaximumSize : function(){
24627         return this.maxSize;
24628     },
24629     
24630     /**
24631      * Sets the maximum size for the resizing element
24632      * @param {Number} maxSize The maximum size
24633      */
24634     setMaximumSize : function(maxSize){
24635         this.maxSize = maxSize;
24636     },
24637     
24638     /**
24639      * Sets the initialize size for the resizing element
24640      * @param {Number} size The initial size
24641      */
24642     setCurrentSize : function(size){
24643         var oldAnimate = this.animate;
24644         this.animate = false;
24645         this.adapter.setElementSize(this, size);
24646         this.animate = oldAnimate;
24647     },
24648     
24649     /**
24650      * Destroy this splitbar. 
24651      * @param {Boolean} removeEl True to remove the element
24652      */
24653     destroy : function(removeEl){
24654         if(this.shim){
24655             this.shim.remove();
24656         }
24657         this.dd.unreg();
24658         this.proxy.parentNode.removeChild(this.proxy);
24659         if(removeEl){
24660             this.el.remove();
24661         }
24662     }
24663 });
24664
24665 /**
24666  * @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.
24667  */
24668 Roo.SplitBar.createProxy = function(dir){
24669     var proxy = new Roo.Element(document.createElement("div"));
24670     proxy.unselectable();
24671     var cls = 'x-splitbar-proxy';
24672     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24673     document.body.appendChild(proxy.dom);
24674     return proxy.dom;
24675 };
24676
24677 /** 
24678  * @class Roo.SplitBar.BasicLayoutAdapter
24679  * Default Adapter. It assumes the splitter and resizing element are not positioned
24680  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24681  */
24682 Roo.SplitBar.BasicLayoutAdapter = function(){
24683 };
24684
24685 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24686     // do nothing for now
24687     init : function(s){
24688     
24689     },
24690     /**
24691      * Called before drag operations to get the current size of the resizing element. 
24692      * @param {Roo.SplitBar} s The SplitBar using this adapter
24693      */
24694      getElementSize : function(s){
24695         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24696             return s.resizingEl.getWidth();
24697         }else{
24698             return s.resizingEl.getHeight();
24699         }
24700     },
24701     
24702     /**
24703      * Called after drag operations to set the size of the resizing element.
24704      * @param {Roo.SplitBar} s The SplitBar using this adapter
24705      * @param {Number} newSize The new size to set
24706      * @param {Function} onComplete A function to be invoked when resizing is complete
24707      */
24708     setElementSize : function(s, newSize, onComplete){
24709         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24710             if(!s.animate){
24711                 s.resizingEl.setWidth(newSize);
24712                 if(onComplete){
24713                     onComplete(s, newSize);
24714                 }
24715             }else{
24716                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24717             }
24718         }else{
24719             
24720             if(!s.animate){
24721                 s.resizingEl.setHeight(newSize);
24722                 if(onComplete){
24723                     onComplete(s, newSize);
24724                 }
24725             }else{
24726                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24727             }
24728         }
24729     }
24730 };
24731
24732 /** 
24733  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24734  * @extends Roo.SplitBar.BasicLayoutAdapter
24735  * Adapter that  moves the splitter element to align with the resized sizing element. 
24736  * Used with an absolute positioned SplitBar.
24737  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24738  * document.body, make sure you assign an id to the body element.
24739  */
24740 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24741     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24742     this.container = Roo.get(container);
24743 };
24744
24745 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24746     init : function(s){
24747         this.basic.init(s);
24748     },
24749     
24750     getElementSize : function(s){
24751         return this.basic.getElementSize(s);
24752     },
24753     
24754     setElementSize : function(s, newSize, onComplete){
24755         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24756     },
24757     
24758     moveSplitter : function(s){
24759         var yes = Roo.SplitBar;
24760         switch(s.placement){
24761             case yes.LEFT:
24762                 s.el.setX(s.resizingEl.getRight());
24763                 break;
24764             case yes.RIGHT:
24765                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24766                 break;
24767             case yes.TOP:
24768                 s.el.setY(s.resizingEl.getBottom());
24769                 break;
24770             case yes.BOTTOM:
24771                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24772                 break;
24773         }
24774     }
24775 };
24776
24777 /**
24778  * Orientation constant - Create a vertical SplitBar
24779  * @static
24780  * @type Number
24781  */
24782 Roo.SplitBar.VERTICAL = 1;
24783
24784 /**
24785  * Orientation constant - Create a horizontal SplitBar
24786  * @static
24787  * @type Number
24788  */
24789 Roo.SplitBar.HORIZONTAL = 2;
24790
24791 /**
24792  * Placement constant - The resizing element is to the left of the splitter element
24793  * @static
24794  * @type Number
24795  */
24796 Roo.SplitBar.LEFT = 1;
24797
24798 /**
24799  * Placement constant - The resizing element is to the right of the splitter element
24800  * @static
24801  * @type Number
24802  */
24803 Roo.SplitBar.RIGHT = 2;
24804
24805 /**
24806  * Placement constant - The resizing element is positioned above the splitter element
24807  * @static
24808  * @type Number
24809  */
24810 Roo.SplitBar.TOP = 3;
24811
24812 /**
24813  * Placement constant - The resizing element is positioned under splitter element
24814  * @static
24815  * @type Number
24816  */
24817 Roo.SplitBar.BOTTOM = 4;
24818 /*
24819  * Based on:
24820  * Ext JS Library 1.1.1
24821  * Copyright(c) 2006-2007, Ext JS, LLC.
24822  *
24823  * Originally Released Under LGPL - original licence link has changed is not relivant.
24824  *
24825  * Fork - LGPL
24826  * <script type="text/javascript">
24827  */
24828
24829 /**
24830  * @class Roo.View
24831  * @extends Roo.util.Observable
24832  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24833  * This class also supports single and multi selection modes. <br>
24834  * Create a data model bound view:
24835  <pre><code>
24836  var store = new Roo.data.Store(...);
24837
24838  var view = new Roo.View({
24839     el : "my-element",
24840     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24841  
24842     singleSelect: true,
24843     selectedClass: "ydataview-selected",
24844     store: store
24845  });
24846
24847  // listen for node click?
24848  view.on("click", function(vw, index, node, e){
24849  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24850  });
24851
24852  // load XML data
24853  dataModel.load("foobar.xml");
24854  </code></pre>
24855  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24856  * <br><br>
24857  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24858  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24859  * 
24860  * Note: old style constructor is still suported (container, template, config)
24861  * 
24862  * @constructor
24863  * Create a new View
24864  * @param {Object} config The config object
24865  * 
24866  */
24867 Roo.View = function(config, depreciated_tpl, depreciated_config){
24868     
24869     this.parent = false;
24870     
24871     if (typeof(depreciated_tpl) == 'undefined') {
24872         // new way.. - universal constructor.
24873         Roo.apply(this, config);
24874         this.el  = Roo.get(this.el);
24875     } else {
24876         // old format..
24877         this.el  = Roo.get(config);
24878         this.tpl = depreciated_tpl;
24879         Roo.apply(this, depreciated_config);
24880     }
24881     this.wrapEl  = this.el.wrap().wrap();
24882     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24883     
24884     
24885     if(typeof(this.tpl) == "string"){
24886         this.tpl = new Roo.Template(this.tpl);
24887     } else {
24888         // support xtype ctors..
24889         this.tpl = new Roo.factory(this.tpl, Roo);
24890     }
24891     
24892     
24893     this.tpl.compile();
24894     
24895     /** @private */
24896     this.addEvents({
24897         /**
24898          * @event beforeclick
24899          * Fires before a click is processed. Returns false to cancel the default action.
24900          * @param {Roo.View} this
24901          * @param {Number} index The index of the target node
24902          * @param {HTMLElement} node The target node
24903          * @param {Roo.EventObject} e The raw event object
24904          */
24905             "beforeclick" : true,
24906         /**
24907          * @event click
24908          * Fires when a template node is clicked.
24909          * @param {Roo.View} this
24910          * @param {Number} index The index of the target node
24911          * @param {HTMLElement} node The target node
24912          * @param {Roo.EventObject} e The raw event object
24913          */
24914             "click" : true,
24915         /**
24916          * @event dblclick
24917          * Fires when a template node is double clicked.
24918          * @param {Roo.View} this
24919          * @param {Number} index The index of the target node
24920          * @param {HTMLElement} node The target node
24921          * @param {Roo.EventObject} e The raw event object
24922          */
24923             "dblclick" : true,
24924         /**
24925          * @event contextmenu
24926          * Fires when a template node is right clicked.
24927          * @param {Roo.View} this
24928          * @param {Number} index The index of the target node
24929          * @param {HTMLElement} node The target node
24930          * @param {Roo.EventObject} e The raw event object
24931          */
24932             "contextmenu" : true,
24933         /**
24934          * @event selectionchange
24935          * Fires when the selected nodes change.
24936          * @param {Roo.View} this
24937          * @param {Array} selections Array of the selected nodes
24938          */
24939             "selectionchange" : true,
24940     
24941         /**
24942          * @event beforeselect
24943          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24944          * @param {Roo.View} this
24945          * @param {HTMLElement} node The node to be selected
24946          * @param {Array} selections Array of currently selected nodes
24947          */
24948             "beforeselect" : true,
24949         /**
24950          * @event preparedata
24951          * Fires on every row to render, to allow you to change the data.
24952          * @param {Roo.View} this
24953          * @param {Object} data to be rendered (change this)
24954          */
24955           "preparedata" : true
24956           
24957           
24958         });
24959
24960
24961
24962     this.el.on({
24963         "click": this.onClick,
24964         "dblclick": this.onDblClick,
24965         "contextmenu": this.onContextMenu,
24966         scope:this
24967     });
24968
24969     this.selections = [];
24970     this.nodes = [];
24971     this.cmp = new Roo.CompositeElementLite([]);
24972     if(this.store){
24973         this.store = Roo.factory(this.store, Roo.data);
24974         this.setStore(this.store, true);
24975     }
24976     
24977     if ( this.footer && this.footer.xtype) {
24978            
24979          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24980         
24981         this.footer.dataSource = this.store
24982         this.footer.container = fctr;
24983         this.footer = Roo.factory(this.footer, Roo);
24984         fctr.insertFirst(this.el);
24985         
24986         // this is a bit insane - as the paging toolbar seems to detach the el..
24987 //        dom.parentNode.parentNode.parentNode
24988          // they get detached?
24989     }
24990     
24991     
24992     Roo.View.superclass.constructor.call(this);
24993     
24994     
24995 };
24996
24997 Roo.extend(Roo.View, Roo.util.Observable, {
24998     
24999      /**
25000      * @cfg {Roo.data.Store} store Data store to load data from.
25001      */
25002     store : false,
25003     
25004     /**
25005      * @cfg {String|Roo.Element} el The container element.
25006      */
25007     el : '',
25008     
25009     /**
25010      * @cfg {String|Roo.Template} tpl The template used by this View 
25011      */
25012     tpl : false,
25013     /**
25014      * @cfg {String} dataName the named area of the template to use as the data area
25015      *                          Works with domtemplates roo-name="name"
25016      */
25017     dataName: false,
25018     /**
25019      * @cfg {String} selectedClass The css class to add to selected nodes
25020      */
25021     selectedClass : "x-view-selected",
25022      /**
25023      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25024      */
25025     emptyText : "",
25026     
25027     /**
25028      * @cfg {String} text to display on mask (default Loading)
25029      */
25030     mask : false,
25031     /**
25032      * @cfg {Boolean} multiSelect Allow multiple selection
25033      */
25034     multiSelect : false,
25035     /**
25036      * @cfg {Boolean} singleSelect Allow single selection
25037      */
25038     singleSelect:  false,
25039     
25040     /**
25041      * @cfg {Boolean} toggleSelect - selecting 
25042      */
25043     toggleSelect : false,
25044     
25045     /**
25046      * @cfg {Boolean} tickable - selecting 
25047      */
25048     tickable : false,
25049     
25050     /**
25051      * Returns the element this view is bound to.
25052      * @return {Roo.Element}
25053      */
25054     getEl : function(){
25055         return this.wrapEl;
25056     },
25057     
25058     
25059
25060     /**
25061      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25062      */
25063     refresh : function(){
25064         //Roo.log('refresh');
25065         var t = this.tpl;
25066         
25067         // if we are using something like 'domtemplate', then
25068         // the what gets used is:
25069         // t.applySubtemplate(NAME, data, wrapping data..)
25070         // the outer template then get' applied with
25071         //     the store 'extra data'
25072         // and the body get's added to the
25073         //      roo-name="data" node?
25074         //      <span class='roo-tpl-{name}'></span> ?????
25075         
25076         
25077         
25078         this.clearSelections();
25079         this.el.update("");
25080         var html = [];
25081         var records = this.store.getRange();
25082         if(records.length < 1) {
25083             
25084             // is this valid??  = should it render a template??
25085             
25086             this.el.update(this.emptyText);
25087             return;
25088         }
25089         var el = this.el;
25090         if (this.dataName) {
25091             this.el.update(t.apply(this.store.meta)); //????
25092             el = this.el.child('.roo-tpl-' + this.dataName);
25093         }
25094         
25095         for(var i = 0, len = records.length; i < len; i++){
25096             var data = this.prepareData(records[i].data, i, records[i]);
25097             this.fireEvent("preparedata", this, data, i, records[i]);
25098             
25099             var d = Roo.apply({}, data);
25100             
25101             if(this.tickable){
25102                 Roo.apply(d, {'roo-id' : Roo.id()});
25103                 
25104                 var _this = this;
25105             
25106                 Roo.each(this.parent.item, function(item){
25107                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25108                         return;
25109                     }
25110                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25111                 });
25112             }
25113             
25114             html[html.length] = Roo.util.Format.trim(
25115                 this.dataName ?
25116                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25117                     t.apply(d)
25118             );
25119         }
25120         
25121         
25122         
25123         el.update(html.join(""));
25124         this.nodes = el.dom.childNodes;
25125         this.updateIndexes(0);
25126     },
25127     
25128
25129     /**
25130      * Function to override to reformat the data that is sent to
25131      * the template for each node.
25132      * DEPRICATED - use the preparedata event handler.
25133      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25134      * a JSON object for an UpdateManager bound view).
25135      */
25136     prepareData : function(data, index, record)
25137     {
25138         this.fireEvent("preparedata", this, data, index, record);
25139         return data;
25140     },
25141
25142     onUpdate : function(ds, record){
25143         // Roo.log('on update');   
25144         this.clearSelections();
25145         var index = this.store.indexOf(record);
25146         var n = this.nodes[index];
25147         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25148         n.parentNode.removeChild(n);
25149         this.updateIndexes(index, index);
25150     },
25151
25152     
25153     
25154 // --------- FIXME     
25155     onAdd : function(ds, records, index)
25156     {
25157         //Roo.log(['on Add', ds, records, index] );        
25158         this.clearSelections();
25159         if(this.nodes.length == 0){
25160             this.refresh();
25161             return;
25162         }
25163         var n = this.nodes[index];
25164         for(var i = 0, len = records.length; i < len; i++){
25165             var d = this.prepareData(records[i].data, i, records[i]);
25166             if(n){
25167                 this.tpl.insertBefore(n, d);
25168             }else{
25169                 
25170                 this.tpl.append(this.el, d);
25171             }
25172         }
25173         this.updateIndexes(index);
25174     },
25175
25176     onRemove : function(ds, record, index){
25177        // Roo.log('onRemove');
25178         this.clearSelections();
25179         var el = this.dataName  ?
25180             this.el.child('.roo-tpl-' + this.dataName) :
25181             this.el; 
25182         
25183         el.dom.removeChild(this.nodes[index]);
25184         this.updateIndexes(index);
25185     },
25186
25187     /**
25188      * Refresh an individual node.
25189      * @param {Number} index
25190      */
25191     refreshNode : function(index){
25192         this.onUpdate(this.store, this.store.getAt(index));
25193     },
25194
25195     updateIndexes : function(startIndex, endIndex){
25196         var ns = this.nodes;
25197         startIndex = startIndex || 0;
25198         endIndex = endIndex || ns.length - 1;
25199         for(var i = startIndex; i <= endIndex; i++){
25200             ns[i].nodeIndex = i;
25201         }
25202     },
25203
25204     /**
25205      * Changes the data store this view uses and refresh the view.
25206      * @param {Store} store
25207      */
25208     setStore : function(store, initial){
25209         if(!initial && this.store){
25210             this.store.un("datachanged", this.refresh);
25211             this.store.un("add", this.onAdd);
25212             this.store.un("remove", this.onRemove);
25213             this.store.un("update", this.onUpdate);
25214             this.store.un("clear", this.refresh);
25215             this.store.un("beforeload", this.onBeforeLoad);
25216             this.store.un("load", this.onLoad);
25217             this.store.un("loadexception", this.onLoad);
25218         }
25219         if(store){
25220           
25221             store.on("datachanged", this.refresh, this);
25222             store.on("add", this.onAdd, this);
25223             store.on("remove", this.onRemove, this);
25224             store.on("update", this.onUpdate, this);
25225             store.on("clear", this.refresh, this);
25226             store.on("beforeload", this.onBeforeLoad, this);
25227             store.on("load", this.onLoad, this);
25228             store.on("loadexception", this.onLoad, this);
25229         }
25230         
25231         if(store){
25232             this.refresh();
25233         }
25234     },
25235     /**
25236      * onbeforeLoad - masks the loading area.
25237      *
25238      */
25239     onBeforeLoad : function(store,opts)
25240     {
25241          //Roo.log('onBeforeLoad');   
25242         if (!opts.add) {
25243             this.el.update("");
25244         }
25245         this.el.mask(this.mask ? this.mask : "Loading" ); 
25246     },
25247     onLoad : function ()
25248     {
25249         this.el.unmask();
25250     },
25251     
25252
25253     /**
25254      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25255      * @param {HTMLElement} node
25256      * @return {HTMLElement} The template node
25257      */
25258     findItemFromChild : function(node){
25259         var el = this.dataName  ?
25260             this.el.child('.roo-tpl-' + this.dataName,true) :
25261             this.el.dom; 
25262         
25263         if(!node || node.parentNode == el){
25264                     return node;
25265             }
25266             var p = node.parentNode;
25267             while(p && p != el){
25268             if(p.parentNode == el){
25269                 return p;
25270             }
25271             p = p.parentNode;
25272         }
25273             return null;
25274     },
25275
25276     /** @ignore */
25277     onClick : function(e){
25278         var item = this.findItemFromChild(e.getTarget());
25279         if(item){
25280             var index = this.indexOf(item);
25281             if(this.onItemClick(item, index, e) !== false){
25282                 this.fireEvent("click", this, index, item, e);
25283             }
25284         }else{
25285             this.clearSelections();
25286         }
25287     },
25288
25289     /** @ignore */
25290     onContextMenu : function(e){
25291         var item = this.findItemFromChild(e.getTarget());
25292         if(item){
25293             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25294         }
25295     },
25296
25297     /** @ignore */
25298     onDblClick : function(e){
25299         var item = this.findItemFromChild(e.getTarget());
25300         if(item){
25301             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25302         }
25303     },
25304
25305     onItemClick : function(item, index, e)
25306     {
25307         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25308             return false;
25309         }
25310         if (this.toggleSelect) {
25311             var m = this.isSelected(item) ? 'unselect' : 'select';
25312             //Roo.log(m);
25313             var _t = this;
25314             _t[m](item, true, false);
25315             return true;
25316         }
25317         if(this.multiSelect || this.singleSelect){
25318             if(this.multiSelect && e.shiftKey && this.lastSelection){
25319                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25320             }else{
25321                 this.select(item, this.multiSelect && e.ctrlKey);
25322                 this.lastSelection = item;
25323             }
25324             
25325             if(!this.tickable){
25326                 e.preventDefault();
25327             }
25328             
25329         }
25330         return true;
25331     },
25332
25333     /**
25334      * Get the number of selected nodes.
25335      * @return {Number}
25336      */
25337     getSelectionCount : function(){
25338         return this.selections.length;
25339     },
25340
25341     /**
25342      * Get the currently selected nodes.
25343      * @return {Array} An array of HTMLElements
25344      */
25345     getSelectedNodes : function(){
25346         return this.selections;
25347     },
25348
25349     /**
25350      * Get the indexes of the selected nodes.
25351      * @return {Array}
25352      */
25353     getSelectedIndexes : function(){
25354         var indexes = [], s = this.selections;
25355         for(var i = 0, len = s.length; i < len; i++){
25356             indexes.push(s[i].nodeIndex);
25357         }
25358         return indexes;
25359     },
25360
25361     /**
25362      * Clear all selections
25363      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25364      */
25365     clearSelections : function(suppressEvent){
25366         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25367             this.cmp.elements = this.selections;
25368             this.cmp.removeClass(this.selectedClass);
25369             this.selections = [];
25370             if(!suppressEvent){
25371                 this.fireEvent("selectionchange", this, this.selections);
25372             }
25373         }
25374     },
25375
25376     /**
25377      * Returns true if the passed node is selected
25378      * @param {HTMLElement/Number} node The node or node index
25379      * @return {Boolean}
25380      */
25381     isSelected : function(node){
25382         var s = this.selections;
25383         if(s.length < 1){
25384             return false;
25385         }
25386         node = this.getNode(node);
25387         return s.indexOf(node) !== -1;
25388     },
25389
25390     /**
25391      * Selects nodes.
25392      * @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
25393      * @param {Boolean} keepExisting (optional) true to keep existing selections
25394      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25395      */
25396     select : function(nodeInfo, keepExisting, suppressEvent){
25397         if(nodeInfo instanceof Array){
25398             if(!keepExisting){
25399                 this.clearSelections(true);
25400             }
25401             for(var i = 0, len = nodeInfo.length; i < len; i++){
25402                 this.select(nodeInfo[i], true, true);
25403             }
25404             return;
25405         } 
25406         var node = this.getNode(nodeInfo);
25407         if(!node || this.isSelected(node)){
25408             return; // already selected.
25409         }
25410         if(!keepExisting){
25411             this.clearSelections(true);
25412         }
25413         
25414         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25415             Roo.fly(node).addClass(this.selectedClass);
25416             this.selections.push(node);
25417             if(!suppressEvent){
25418                 this.fireEvent("selectionchange", this, this.selections);
25419             }
25420         }
25421         
25422         
25423     },
25424       /**
25425      * Unselects nodes.
25426      * @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
25427      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25428      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25429      */
25430     unselect : function(nodeInfo, keepExisting, suppressEvent)
25431     {
25432         if(nodeInfo instanceof Array){
25433             Roo.each(this.selections, function(s) {
25434                 this.unselect(s, nodeInfo);
25435             }, this);
25436             return;
25437         }
25438         var node = this.getNode(nodeInfo);
25439         if(!node || !this.isSelected(node)){
25440             //Roo.log("not selected");
25441             return; // not selected.
25442         }
25443         // fireevent???
25444         var ns = [];
25445         Roo.each(this.selections, function(s) {
25446             if (s == node ) {
25447                 Roo.fly(node).removeClass(this.selectedClass);
25448
25449                 return;
25450             }
25451             ns.push(s);
25452         },this);
25453         
25454         this.selections= ns;
25455         this.fireEvent("selectionchange", this, this.selections);
25456     },
25457
25458     /**
25459      * Gets a template node.
25460      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25461      * @return {HTMLElement} The node or null if it wasn't found
25462      */
25463     getNode : function(nodeInfo){
25464         if(typeof nodeInfo == "string"){
25465             return document.getElementById(nodeInfo);
25466         }else if(typeof nodeInfo == "number"){
25467             return this.nodes[nodeInfo];
25468         }
25469         return nodeInfo;
25470     },
25471
25472     /**
25473      * Gets a range template nodes.
25474      * @param {Number} startIndex
25475      * @param {Number} endIndex
25476      * @return {Array} An array of nodes
25477      */
25478     getNodes : function(start, end){
25479         var ns = this.nodes;
25480         start = start || 0;
25481         end = typeof end == "undefined" ? ns.length - 1 : end;
25482         var nodes = [];
25483         if(start <= end){
25484             for(var i = start; i <= end; i++){
25485                 nodes.push(ns[i]);
25486             }
25487         } else{
25488             for(var i = start; i >= end; i--){
25489                 nodes.push(ns[i]);
25490             }
25491         }
25492         return nodes;
25493     },
25494
25495     /**
25496      * Finds the index of the passed node
25497      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25498      * @return {Number} The index of the node or -1
25499      */
25500     indexOf : function(node){
25501         node = this.getNode(node);
25502         if(typeof node.nodeIndex == "number"){
25503             return node.nodeIndex;
25504         }
25505         var ns = this.nodes;
25506         for(var i = 0, len = ns.length; i < len; i++){
25507             if(ns[i] == node){
25508                 return i;
25509             }
25510         }
25511         return -1;
25512     }
25513 });
25514 /*
25515  * Based on:
25516  * Ext JS Library 1.1.1
25517  * Copyright(c) 2006-2007, Ext JS, LLC.
25518  *
25519  * Originally Released Under LGPL - original licence link has changed is not relivant.
25520  *
25521  * Fork - LGPL
25522  * <script type="text/javascript">
25523  */
25524
25525 /**
25526  * @class Roo.JsonView
25527  * @extends Roo.View
25528  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25529 <pre><code>
25530 var view = new Roo.JsonView({
25531     container: "my-element",
25532     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25533     multiSelect: true, 
25534     jsonRoot: "data" 
25535 });
25536
25537 // listen for node click?
25538 view.on("click", function(vw, index, node, e){
25539     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25540 });
25541
25542 // direct load of JSON data
25543 view.load("foobar.php");
25544
25545 // Example from my blog list
25546 var tpl = new Roo.Template(
25547     '&lt;div class="entry"&gt;' +
25548     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25549     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25550     "&lt;/div&gt;&lt;hr /&gt;"
25551 );
25552
25553 var moreView = new Roo.JsonView({
25554     container :  "entry-list", 
25555     template : tpl,
25556     jsonRoot: "posts"
25557 });
25558 moreView.on("beforerender", this.sortEntries, this);
25559 moreView.load({
25560     url: "/blog/get-posts.php",
25561     params: "allposts=true",
25562     text: "Loading Blog Entries..."
25563 });
25564 </code></pre>
25565
25566 * Note: old code is supported with arguments : (container, template, config)
25567
25568
25569  * @constructor
25570  * Create a new JsonView
25571  * 
25572  * @param {Object} config The config object
25573  * 
25574  */
25575 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25576     
25577     
25578     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25579
25580     var um = this.el.getUpdateManager();
25581     um.setRenderer(this);
25582     um.on("update", this.onLoad, this);
25583     um.on("failure", this.onLoadException, this);
25584
25585     /**
25586      * @event beforerender
25587      * Fires before rendering of the downloaded JSON data.
25588      * @param {Roo.JsonView} this
25589      * @param {Object} data The JSON data loaded
25590      */
25591     /**
25592      * @event load
25593      * Fires when data is loaded.
25594      * @param {Roo.JsonView} this
25595      * @param {Object} data The JSON data loaded
25596      * @param {Object} response The raw Connect response object
25597      */
25598     /**
25599      * @event loadexception
25600      * Fires when loading fails.
25601      * @param {Roo.JsonView} this
25602      * @param {Object} response The raw Connect response object
25603      */
25604     this.addEvents({
25605         'beforerender' : true,
25606         'load' : true,
25607         'loadexception' : true
25608     });
25609 };
25610 Roo.extend(Roo.JsonView, Roo.View, {
25611     /**
25612      * @type {String} The root property in the loaded JSON object that contains the data
25613      */
25614     jsonRoot : "",
25615
25616     /**
25617      * Refreshes the view.
25618      */
25619     refresh : function(){
25620         this.clearSelections();
25621         this.el.update("");
25622         var html = [];
25623         var o = this.jsonData;
25624         if(o && o.length > 0){
25625             for(var i = 0, len = o.length; i < len; i++){
25626                 var data = this.prepareData(o[i], i, o);
25627                 html[html.length] = this.tpl.apply(data);
25628             }
25629         }else{
25630             html.push(this.emptyText);
25631         }
25632         this.el.update(html.join(""));
25633         this.nodes = this.el.dom.childNodes;
25634         this.updateIndexes(0);
25635     },
25636
25637     /**
25638      * 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.
25639      * @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:
25640      <pre><code>
25641      view.load({
25642          url: "your-url.php",
25643          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25644          callback: yourFunction,
25645          scope: yourObject, //(optional scope)
25646          discardUrl: false,
25647          nocache: false,
25648          text: "Loading...",
25649          timeout: 30,
25650          scripts: false
25651      });
25652      </code></pre>
25653      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25654      * 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.
25655      * @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}
25656      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25657      * @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.
25658      */
25659     load : function(){
25660         var um = this.el.getUpdateManager();
25661         um.update.apply(um, arguments);
25662     },
25663
25664     render : function(el, response){
25665         this.clearSelections();
25666         this.el.update("");
25667         var o;
25668         try{
25669             o = Roo.util.JSON.decode(response.responseText);
25670             if(this.jsonRoot){
25671                 
25672                 o = o[this.jsonRoot];
25673             }
25674         } catch(e){
25675         }
25676         /**
25677          * The current JSON data or null
25678          */
25679         this.jsonData = o;
25680         this.beforeRender();
25681         this.refresh();
25682     },
25683
25684 /**
25685  * Get the number of records in the current JSON dataset
25686  * @return {Number}
25687  */
25688     getCount : function(){
25689         return this.jsonData ? this.jsonData.length : 0;
25690     },
25691
25692 /**
25693  * Returns the JSON object for the specified node(s)
25694  * @param {HTMLElement/Array} node The node or an array of nodes
25695  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25696  * you get the JSON object for the node
25697  */
25698     getNodeData : function(node){
25699         if(node instanceof Array){
25700             var data = [];
25701             for(var i = 0, len = node.length; i < len; i++){
25702                 data.push(this.getNodeData(node[i]));
25703             }
25704             return data;
25705         }
25706         return this.jsonData[this.indexOf(node)] || null;
25707     },
25708
25709     beforeRender : function(){
25710         this.snapshot = this.jsonData;
25711         if(this.sortInfo){
25712             this.sort.apply(this, this.sortInfo);
25713         }
25714         this.fireEvent("beforerender", this, this.jsonData);
25715     },
25716
25717     onLoad : function(el, o){
25718         this.fireEvent("load", this, this.jsonData, o);
25719     },
25720
25721     onLoadException : function(el, o){
25722         this.fireEvent("loadexception", this, o);
25723     },
25724
25725 /**
25726  * Filter the data by a specific property.
25727  * @param {String} property A property on your JSON objects
25728  * @param {String/RegExp} value Either string that the property values
25729  * should start with, or a RegExp to test against the property
25730  */
25731     filter : function(property, value){
25732         if(this.jsonData){
25733             var data = [];
25734             var ss = this.snapshot;
25735             if(typeof value == "string"){
25736                 var vlen = value.length;
25737                 if(vlen == 0){
25738                     this.clearFilter();
25739                     return;
25740                 }
25741                 value = value.toLowerCase();
25742                 for(var i = 0, len = ss.length; i < len; i++){
25743                     var o = ss[i];
25744                     if(o[property].substr(0, vlen).toLowerCase() == value){
25745                         data.push(o);
25746                     }
25747                 }
25748             } else if(value.exec){ // regex?
25749                 for(var i = 0, len = ss.length; i < len; i++){
25750                     var o = ss[i];
25751                     if(value.test(o[property])){
25752                         data.push(o);
25753                     }
25754                 }
25755             } else{
25756                 return;
25757             }
25758             this.jsonData = data;
25759             this.refresh();
25760         }
25761     },
25762
25763 /**
25764  * Filter by a function. The passed function will be called with each
25765  * object in the current dataset. If the function returns true the value is kept,
25766  * otherwise it is filtered.
25767  * @param {Function} fn
25768  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25769  */
25770     filterBy : function(fn, scope){
25771         if(this.jsonData){
25772             var data = [];
25773             var ss = this.snapshot;
25774             for(var i = 0, len = ss.length; i < len; i++){
25775                 var o = ss[i];
25776                 if(fn.call(scope || this, o)){
25777                     data.push(o);
25778                 }
25779             }
25780             this.jsonData = data;
25781             this.refresh();
25782         }
25783     },
25784
25785 /**
25786  * Clears the current filter.
25787  */
25788     clearFilter : function(){
25789         if(this.snapshot && this.jsonData != this.snapshot){
25790             this.jsonData = this.snapshot;
25791             this.refresh();
25792         }
25793     },
25794
25795
25796 /**
25797  * Sorts the data for this view and refreshes it.
25798  * @param {String} property A property on your JSON objects to sort on
25799  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25800  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25801  */
25802     sort : function(property, dir, sortType){
25803         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25804         if(this.jsonData){
25805             var p = property;
25806             var dsc = dir && dir.toLowerCase() == "desc";
25807             var f = function(o1, o2){
25808                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25809                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25810                 ;
25811                 if(v1 < v2){
25812                     return dsc ? +1 : -1;
25813                 } else if(v1 > v2){
25814                     return dsc ? -1 : +1;
25815                 } else{
25816                     return 0;
25817                 }
25818             };
25819             this.jsonData.sort(f);
25820             this.refresh();
25821             if(this.jsonData != this.snapshot){
25822                 this.snapshot.sort(f);
25823             }
25824         }
25825     }
25826 });/*
25827  * Based on:
25828  * Ext JS Library 1.1.1
25829  * Copyright(c) 2006-2007, Ext JS, LLC.
25830  *
25831  * Originally Released Under LGPL - original licence link has changed is not relivant.
25832  *
25833  * Fork - LGPL
25834  * <script type="text/javascript">
25835  */
25836  
25837
25838 /**
25839  * @class Roo.ColorPalette
25840  * @extends Roo.Component
25841  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25842  * Here's an example of typical usage:
25843  * <pre><code>
25844 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25845 cp.render('my-div');
25846
25847 cp.on('select', function(palette, selColor){
25848     // do something with selColor
25849 });
25850 </code></pre>
25851  * @constructor
25852  * Create a new ColorPalette
25853  * @param {Object} config The config object
25854  */
25855 Roo.ColorPalette = function(config){
25856     Roo.ColorPalette.superclass.constructor.call(this, config);
25857     this.addEvents({
25858         /**
25859              * @event select
25860              * Fires when a color is selected
25861              * @param {ColorPalette} this
25862              * @param {String} color The 6-digit color hex code (without the # symbol)
25863              */
25864         select: true
25865     });
25866
25867     if(this.handler){
25868         this.on("select", this.handler, this.scope, true);
25869     }
25870 };
25871 Roo.extend(Roo.ColorPalette, Roo.Component, {
25872     /**
25873      * @cfg {String} itemCls
25874      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25875      */
25876     itemCls : "x-color-palette",
25877     /**
25878      * @cfg {String} value
25879      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25880      * the hex codes are case-sensitive.
25881      */
25882     value : null,
25883     clickEvent:'click',
25884     // private
25885     ctype: "Roo.ColorPalette",
25886
25887     /**
25888      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25889      */
25890     allowReselect : false,
25891
25892     /**
25893      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25894      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25895      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25896      * of colors with the width setting until the box is symmetrical.</p>
25897      * <p>You can override individual colors if needed:</p>
25898      * <pre><code>
25899 var cp = new Roo.ColorPalette();
25900 cp.colors[0] = "FF0000";  // change the first box to red
25901 </code></pre>
25902
25903 Or you can provide a custom array of your own for complete control:
25904 <pre><code>
25905 var cp = new Roo.ColorPalette();
25906 cp.colors = ["000000", "993300", "333300"];
25907 </code></pre>
25908      * @type Array
25909      */
25910     colors : [
25911         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25912         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25913         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25914         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25915         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25916     ],
25917
25918     // private
25919     onRender : function(container, position){
25920         var t = new Roo.MasterTemplate(
25921             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25922         );
25923         var c = this.colors;
25924         for(var i = 0, len = c.length; i < len; i++){
25925             t.add([c[i]]);
25926         }
25927         var el = document.createElement("div");
25928         el.className = this.itemCls;
25929         t.overwrite(el);
25930         container.dom.insertBefore(el, position);
25931         this.el = Roo.get(el);
25932         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25933         if(this.clickEvent != 'click'){
25934             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25935         }
25936     },
25937
25938     // private
25939     afterRender : function(){
25940         Roo.ColorPalette.superclass.afterRender.call(this);
25941         if(this.value){
25942             var s = this.value;
25943             this.value = null;
25944             this.select(s);
25945         }
25946     },
25947
25948     // private
25949     handleClick : function(e, t){
25950         e.preventDefault();
25951         if(!this.disabled){
25952             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25953             this.select(c.toUpperCase());
25954         }
25955     },
25956
25957     /**
25958      * Selects the specified color in the palette (fires the select event)
25959      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25960      */
25961     select : function(color){
25962         color = color.replace("#", "");
25963         if(color != this.value || this.allowReselect){
25964             var el = this.el;
25965             if(this.value){
25966                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25967             }
25968             el.child("a.color-"+color).addClass("x-color-palette-sel");
25969             this.value = color;
25970             this.fireEvent("select", this, color);
25971         }
25972     }
25973 });/*
25974  * Based on:
25975  * Ext JS Library 1.1.1
25976  * Copyright(c) 2006-2007, Ext JS, LLC.
25977  *
25978  * Originally Released Under LGPL - original licence link has changed is not relivant.
25979  *
25980  * Fork - LGPL
25981  * <script type="text/javascript">
25982  */
25983  
25984 /**
25985  * @class Roo.DatePicker
25986  * @extends Roo.Component
25987  * Simple date picker class.
25988  * @constructor
25989  * Create a new DatePicker
25990  * @param {Object} config The config object
25991  */
25992 Roo.DatePicker = function(config){
25993     Roo.DatePicker.superclass.constructor.call(this, config);
25994
25995     this.value = config && config.value ?
25996                  config.value.clearTime() : new Date().clearTime();
25997
25998     this.addEvents({
25999         /**
26000              * @event select
26001              * Fires when a date is selected
26002              * @param {DatePicker} this
26003              * @param {Date} date The selected date
26004              */
26005         'select': true,
26006         /**
26007              * @event monthchange
26008              * Fires when the displayed month changes 
26009              * @param {DatePicker} this
26010              * @param {Date} date The selected month
26011              */
26012         'monthchange': true
26013     });
26014
26015     if(this.handler){
26016         this.on("select", this.handler,  this.scope || this);
26017     }
26018     // build the disabledDatesRE
26019     if(!this.disabledDatesRE && this.disabledDates){
26020         var dd = this.disabledDates;
26021         var re = "(?:";
26022         for(var i = 0; i < dd.length; i++){
26023             re += dd[i];
26024             if(i != dd.length-1) re += "|";
26025         }
26026         this.disabledDatesRE = new RegExp(re + ")");
26027     }
26028 };
26029
26030 Roo.extend(Roo.DatePicker, Roo.Component, {
26031     /**
26032      * @cfg {String} todayText
26033      * The text to display on the button that selects the current date (defaults to "Today")
26034      */
26035     todayText : "Today",
26036     /**
26037      * @cfg {String} okText
26038      * The text to display on the ok button
26039      */
26040     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26041     /**
26042      * @cfg {String} cancelText
26043      * The text to display on the cancel button
26044      */
26045     cancelText : "Cancel",
26046     /**
26047      * @cfg {String} todayTip
26048      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26049      */
26050     todayTip : "{0} (Spacebar)",
26051     /**
26052      * @cfg {Date} minDate
26053      * Minimum allowable date (JavaScript date object, defaults to null)
26054      */
26055     minDate : null,
26056     /**
26057      * @cfg {Date} maxDate
26058      * Maximum allowable date (JavaScript date object, defaults to null)
26059      */
26060     maxDate : null,
26061     /**
26062      * @cfg {String} minText
26063      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26064      */
26065     minText : "This date is before the minimum date",
26066     /**
26067      * @cfg {String} maxText
26068      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26069      */
26070     maxText : "This date is after the maximum date",
26071     /**
26072      * @cfg {String} format
26073      * The default date format string which can be overriden for localization support.  The format must be
26074      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26075      */
26076     format : "m/d/y",
26077     /**
26078      * @cfg {Array} disabledDays
26079      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26080      */
26081     disabledDays : null,
26082     /**
26083      * @cfg {String} disabledDaysText
26084      * The tooltip to display when the date falls on a disabled day (defaults to "")
26085      */
26086     disabledDaysText : "",
26087     /**
26088      * @cfg {RegExp} disabledDatesRE
26089      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26090      */
26091     disabledDatesRE : null,
26092     /**
26093      * @cfg {String} disabledDatesText
26094      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26095      */
26096     disabledDatesText : "",
26097     /**
26098      * @cfg {Boolean} constrainToViewport
26099      * True to constrain the date picker to the viewport (defaults to true)
26100      */
26101     constrainToViewport : true,
26102     /**
26103      * @cfg {Array} monthNames
26104      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26105      */
26106     monthNames : Date.monthNames,
26107     /**
26108      * @cfg {Array} dayNames
26109      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26110      */
26111     dayNames : Date.dayNames,
26112     /**
26113      * @cfg {String} nextText
26114      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26115      */
26116     nextText: 'Next Month (Control+Right)',
26117     /**
26118      * @cfg {String} prevText
26119      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26120      */
26121     prevText: 'Previous Month (Control+Left)',
26122     /**
26123      * @cfg {String} monthYearText
26124      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26125      */
26126     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26127     /**
26128      * @cfg {Number} startDay
26129      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26130      */
26131     startDay : 0,
26132     /**
26133      * @cfg {Bool} showClear
26134      * Show a clear button (usefull for date form elements that can be blank.)
26135      */
26136     
26137     showClear: false,
26138     
26139     /**
26140      * Sets the value of the date field
26141      * @param {Date} value The date to set
26142      */
26143     setValue : function(value){
26144         var old = this.value;
26145         
26146         if (typeof(value) == 'string') {
26147          
26148             value = Date.parseDate(value, this.format);
26149         }
26150         if (!value) {
26151             value = new Date();
26152         }
26153         
26154         this.value = value.clearTime(true);
26155         if(this.el){
26156             this.update(this.value);
26157         }
26158     },
26159
26160     /**
26161      * Gets the current selected value of the date field
26162      * @return {Date} The selected date
26163      */
26164     getValue : function(){
26165         return this.value;
26166     },
26167
26168     // private
26169     focus : function(){
26170         if(this.el){
26171             this.update(this.activeDate);
26172         }
26173     },
26174
26175     // privateval
26176     onRender : function(container, position){
26177         
26178         var m = [
26179              '<table cellspacing="0">',
26180                 '<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>',
26181                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26182         var dn = this.dayNames;
26183         for(var i = 0; i < 7; i++){
26184             var d = this.startDay+i;
26185             if(d > 6){
26186                 d = d-7;
26187             }
26188             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26189         }
26190         m[m.length] = "</tr></thead><tbody><tr>";
26191         for(var i = 0; i < 42; i++) {
26192             if(i % 7 == 0 && i != 0){
26193                 m[m.length] = "</tr><tr>";
26194             }
26195             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26196         }
26197         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26198             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26199
26200         var el = document.createElement("div");
26201         el.className = "x-date-picker";
26202         el.innerHTML = m.join("");
26203
26204         container.dom.insertBefore(el, position);
26205
26206         this.el = Roo.get(el);
26207         this.eventEl = Roo.get(el.firstChild);
26208
26209         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26210             handler: this.showPrevMonth,
26211             scope: this,
26212             preventDefault:true,
26213             stopDefault:true
26214         });
26215
26216         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26217             handler: this.showNextMonth,
26218             scope: this,
26219             preventDefault:true,
26220             stopDefault:true
26221         });
26222
26223         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26224
26225         this.monthPicker = this.el.down('div.x-date-mp');
26226         this.monthPicker.enableDisplayMode('block');
26227         
26228         var kn = new Roo.KeyNav(this.eventEl, {
26229             "left" : function(e){
26230                 e.ctrlKey ?
26231                     this.showPrevMonth() :
26232                     this.update(this.activeDate.add("d", -1));
26233             },
26234
26235             "right" : function(e){
26236                 e.ctrlKey ?
26237                     this.showNextMonth() :
26238                     this.update(this.activeDate.add("d", 1));
26239             },
26240
26241             "up" : function(e){
26242                 e.ctrlKey ?
26243                     this.showNextYear() :
26244                     this.update(this.activeDate.add("d", -7));
26245             },
26246
26247             "down" : function(e){
26248                 e.ctrlKey ?
26249                     this.showPrevYear() :
26250                     this.update(this.activeDate.add("d", 7));
26251             },
26252
26253             "pageUp" : function(e){
26254                 this.showNextMonth();
26255             },
26256
26257             "pageDown" : function(e){
26258                 this.showPrevMonth();
26259             },
26260
26261             "enter" : function(e){
26262                 e.stopPropagation();
26263                 return true;
26264             },
26265
26266             scope : this
26267         });
26268
26269         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26270
26271         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26272
26273         this.el.unselectable();
26274         
26275         this.cells = this.el.select("table.x-date-inner tbody td");
26276         this.textNodes = this.el.query("table.x-date-inner tbody span");
26277
26278         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26279             text: "&#160;",
26280             tooltip: this.monthYearText
26281         });
26282
26283         this.mbtn.on('click', this.showMonthPicker, this);
26284         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26285
26286
26287         var today = (new Date()).dateFormat(this.format);
26288         
26289         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26290         if (this.showClear) {
26291             baseTb.add( new Roo.Toolbar.Fill());
26292         }
26293         baseTb.add({
26294             text: String.format(this.todayText, today),
26295             tooltip: String.format(this.todayTip, today),
26296             handler: this.selectToday,
26297             scope: this
26298         });
26299         
26300         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26301             
26302         //});
26303         if (this.showClear) {
26304             
26305             baseTb.add( new Roo.Toolbar.Fill());
26306             baseTb.add({
26307                 text: '&#160;',
26308                 cls: 'x-btn-icon x-btn-clear',
26309                 handler: function() {
26310                     //this.value = '';
26311                     this.fireEvent("select", this, '');
26312                 },
26313                 scope: this
26314             });
26315         }
26316         
26317         
26318         if(Roo.isIE){
26319             this.el.repaint();
26320         }
26321         this.update(this.value);
26322     },
26323
26324     createMonthPicker : function(){
26325         if(!this.monthPicker.dom.firstChild){
26326             var buf = ['<table border="0" cellspacing="0">'];
26327             for(var i = 0; i < 6; i++){
26328                 buf.push(
26329                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26330                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26331                     i == 0 ?
26332                     '<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>' :
26333                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26334                 );
26335             }
26336             buf.push(
26337                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26338                     this.okText,
26339                     '</button><button type="button" class="x-date-mp-cancel">',
26340                     this.cancelText,
26341                     '</button></td></tr>',
26342                 '</table>'
26343             );
26344             this.monthPicker.update(buf.join(''));
26345             this.monthPicker.on('click', this.onMonthClick, this);
26346             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26347
26348             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26349             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26350
26351             this.mpMonths.each(function(m, a, i){
26352                 i += 1;
26353                 if((i%2) == 0){
26354                     m.dom.xmonth = 5 + Math.round(i * .5);
26355                 }else{
26356                     m.dom.xmonth = Math.round((i-1) * .5);
26357                 }
26358             });
26359         }
26360     },
26361
26362     showMonthPicker : function(){
26363         this.createMonthPicker();
26364         var size = this.el.getSize();
26365         this.monthPicker.setSize(size);
26366         this.monthPicker.child('table').setSize(size);
26367
26368         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26369         this.updateMPMonth(this.mpSelMonth);
26370         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26371         this.updateMPYear(this.mpSelYear);
26372
26373         this.monthPicker.slideIn('t', {duration:.2});
26374     },
26375
26376     updateMPYear : function(y){
26377         this.mpyear = y;
26378         var ys = this.mpYears.elements;
26379         for(var i = 1; i <= 10; i++){
26380             var td = ys[i-1], y2;
26381             if((i%2) == 0){
26382                 y2 = y + Math.round(i * .5);
26383                 td.firstChild.innerHTML = y2;
26384                 td.xyear = y2;
26385             }else{
26386                 y2 = y - (5-Math.round(i * .5));
26387                 td.firstChild.innerHTML = y2;
26388                 td.xyear = y2;
26389             }
26390             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26391         }
26392     },
26393
26394     updateMPMonth : function(sm){
26395         this.mpMonths.each(function(m, a, i){
26396             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26397         });
26398     },
26399
26400     selectMPMonth: function(m){
26401         
26402     },
26403
26404     onMonthClick : function(e, t){
26405         e.stopEvent();
26406         var el = new Roo.Element(t), pn;
26407         if(el.is('button.x-date-mp-cancel')){
26408             this.hideMonthPicker();
26409         }
26410         else if(el.is('button.x-date-mp-ok')){
26411             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26412             this.hideMonthPicker();
26413         }
26414         else if(pn = el.up('td.x-date-mp-month', 2)){
26415             this.mpMonths.removeClass('x-date-mp-sel');
26416             pn.addClass('x-date-mp-sel');
26417             this.mpSelMonth = pn.dom.xmonth;
26418         }
26419         else if(pn = el.up('td.x-date-mp-year', 2)){
26420             this.mpYears.removeClass('x-date-mp-sel');
26421             pn.addClass('x-date-mp-sel');
26422             this.mpSelYear = pn.dom.xyear;
26423         }
26424         else if(el.is('a.x-date-mp-prev')){
26425             this.updateMPYear(this.mpyear-10);
26426         }
26427         else if(el.is('a.x-date-mp-next')){
26428             this.updateMPYear(this.mpyear+10);
26429         }
26430     },
26431
26432     onMonthDblClick : function(e, t){
26433         e.stopEvent();
26434         var el = new Roo.Element(t), pn;
26435         if(pn = el.up('td.x-date-mp-month', 2)){
26436             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26437             this.hideMonthPicker();
26438         }
26439         else if(pn = el.up('td.x-date-mp-year', 2)){
26440             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26441             this.hideMonthPicker();
26442         }
26443     },
26444
26445     hideMonthPicker : function(disableAnim){
26446         if(this.monthPicker){
26447             if(disableAnim === true){
26448                 this.monthPicker.hide();
26449             }else{
26450                 this.monthPicker.slideOut('t', {duration:.2});
26451             }
26452         }
26453     },
26454
26455     // private
26456     showPrevMonth : function(e){
26457         this.update(this.activeDate.add("mo", -1));
26458     },
26459
26460     // private
26461     showNextMonth : function(e){
26462         this.update(this.activeDate.add("mo", 1));
26463     },
26464
26465     // private
26466     showPrevYear : function(){
26467         this.update(this.activeDate.add("y", -1));
26468     },
26469
26470     // private
26471     showNextYear : function(){
26472         this.update(this.activeDate.add("y", 1));
26473     },
26474
26475     // private
26476     handleMouseWheel : function(e){
26477         var delta = e.getWheelDelta();
26478         if(delta > 0){
26479             this.showPrevMonth();
26480             e.stopEvent();
26481         } else if(delta < 0){
26482             this.showNextMonth();
26483             e.stopEvent();
26484         }
26485     },
26486
26487     // private
26488     handleDateClick : function(e, t){
26489         e.stopEvent();
26490         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26491             this.setValue(new Date(t.dateValue));
26492             this.fireEvent("select", this, this.value);
26493         }
26494     },
26495
26496     // private
26497     selectToday : function(){
26498         this.setValue(new Date().clearTime());
26499         this.fireEvent("select", this, this.value);
26500     },
26501
26502     // private
26503     update : function(date)
26504     {
26505         var vd = this.activeDate;
26506         this.activeDate = date;
26507         if(vd && this.el){
26508             var t = date.getTime();
26509             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26510                 this.cells.removeClass("x-date-selected");
26511                 this.cells.each(function(c){
26512                    if(c.dom.firstChild.dateValue == t){
26513                        c.addClass("x-date-selected");
26514                        setTimeout(function(){
26515                             try{c.dom.firstChild.focus();}catch(e){}
26516                        }, 50);
26517                        return false;
26518                    }
26519                 });
26520                 return;
26521             }
26522         }
26523         
26524         var days = date.getDaysInMonth();
26525         var firstOfMonth = date.getFirstDateOfMonth();
26526         var startingPos = firstOfMonth.getDay()-this.startDay;
26527
26528         if(startingPos <= this.startDay){
26529             startingPos += 7;
26530         }
26531
26532         var pm = date.add("mo", -1);
26533         var prevStart = pm.getDaysInMonth()-startingPos;
26534
26535         var cells = this.cells.elements;
26536         var textEls = this.textNodes;
26537         days += startingPos;
26538
26539         // convert everything to numbers so it's fast
26540         var day = 86400000;
26541         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26542         var today = new Date().clearTime().getTime();
26543         var sel = date.clearTime().getTime();
26544         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26545         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26546         var ddMatch = this.disabledDatesRE;
26547         var ddText = this.disabledDatesText;
26548         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26549         var ddaysText = this.disabledDaysText;
26550         var format = this.format;
26551
26552         var setCellClass = function(cal, cell){
26553             cell.title = "";
26554             var t = d.getTime();
26555             cell.firstChild.dateValue = t;
26556             if(t == today){
26557                 cell.className += " x-date-today";
26558                 cell.title = cal.todayText;
26559             }
26560             if(t == sel){
26561                 cell.className += " x-date-selected";
26562                 setTimeout(function(){
26563                     try{cell.firstChild.focus();}catch(e){}
26564                 }, 50);
26565             }
26566             // disabling
26567             if(t < min) {
26568                 cell.className = " x-date-disabled";
26569                 cell.title = cal.minText;
26570                 return;
26571             }
26572             if(t > max) {
26573                 cell.className = " x-date-disabled";
26574                 cell.title = cal.maxText;
26575                 return;
26576             }
26577             if(ddays){
26578                 if(ddays.indexOf(d.getDay()) != -1){
26579                     cell.title = ddaysText;
26580                     cell.className = " x-date-disabled";
26581                 }
26582             }
26583             if(ddMatch && format){
26584                 var fvalue = d.dateFormat(format);
26585                 if(ddMatch.test(fvalue)){
26586                     cell.title = ddText.replace("%0", fvalue);
26587                     cell.className = " x-date-disabled";
26588                 }
26589             }
26590         };
26591
26592         var i = 0;
26593         for(; i < startingPos; i++) {
26594             textEls[i].innerHTML = (++prevStart);
26595             d.setDate(d.getDate()+1);
26596             cells[i].className = "x-date-prevday";
26597             setCellClass(this, cells[i]);
26598         }
26599         for(; i < days; i++){
26600             intDay = i - startingPos + 1;
26601             textEls[i].innerHTML = (intDay);
26602             d.setDate(d.getDate()+1);
26603             cells[i].className = "x-date-active";
26604             setCellClass(this, cells[i]);
26605         }
26606         var extraDays = 0;
26607         for(; i < 42; i++) {
26608              textEls[i].innerHTML = (++extraDays);
26609              d.setDate(d.getDate()+1);
26610              cells[i].className = "x-date-nextday";
26611              setCellClass(this, cells[i]);
26612         }
26613
26614         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26615         this.fireEvent('monthchange', this, date);
26616         
26617         if(!this.internalRender){
26618             var main = this.el.dom.firstChild;
26619             var w = main.offsetWidth;
26620             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26621             Roo.fly(main).setWidth(w);
26622             this.internalRender = true;
26623             // opera does not respect the auto grow header center column
26624             // then, after it gets a width opera refuses to recalculate
26625             // without a second pass
26626             if(Roo.isOpera && !this.secondPass){
26627                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26628                 this.secondPass = true;
26629                 this.update.defer(10, this, [date]);
26630             }
26631         }
26632         
26633         
26634     }
26635 });        /*
26636  * Based on:
26637  * Ext JS Library 1.1.1
26638  * Copyright(c) 2006-2007, Ext JS, LLC.
26639  *
26640  * Originally Released Under LGPL - original licence link has changed is not relivant.
26641  *
26642  * Fork - LGPL
26643  * <script type="text/javascript">
26644  */
26645 /**
26646  * @class Roo.TabPanel
26647  * @extends Roo.util.Observable
26648  * A lightweight tab container.
26649  * <br><br>
26650  * Usage:
26651  * <pre><code>
26652 // basic tabs 1, built from existing content
26653 var tabs = new Roo.TabPanel("tabs1");
26654 tabs.addTab("script", "View Script");
26655 tabs.addTab("markup", "View Markup");
26656 tabs.activate("script");
26657
26658 // more advanced tabs, built from javascript
26659 var jtabs = new Roo.TabPanel("jtabs");
26660 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26661
26662 // set up the UpdateManager
26663 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26664 var updater = tab2.getUpdateManager();
26665 updater.setDefaultUrl("ajax1.htm");
26666 tab2.on('activate', updater.refresh, updater, true);
26667
26668 // Use setUrl for Ajax loading
26669 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26670 tab3.setUrl("ajax2.htm", null, true);
26671
26672 // Disabled tab
26673 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26674 tab4.disable();
26675
26676 jtabs.activate("jtabs-1");
26677  * </code></pre>
26678  * @constructor
26679  * Create a new TabPanel.
26680  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26681  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26682  */
26683 Roo.TabPanel = function(container, config){
26684     /**
26685     * The container element for this TabPanel.
26686     * @type Roo.Element
26687     */
26688     this.el = Roo.get(container, true);
26689     if(config){
26690         if(typeof config == "boolean"){
26691             this.tabPosition = config ? "bottom" : "top";
26692         }else{
26693             Roo.apply(this, config);
26694         }
26695     }
26696     if(this.tabPosition == "bottom"){
26697         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26698         this.el.addClass("x-tabs-bottom");
26699     }
26700     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26701     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26702     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26703     if(Roo.isIE){
26704         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26705     }
26706     if(this.tabPosition != "bottom"){
26707         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26708          * @type Roo.Element
26709          */
26710         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26711         this.el.addClass("x-tabs-top");
26712     }
26713     this.items = [];
26714
26715     this.bodyEl.setStyle("position", "relative");
26716
26717     this.active = null;
26718     this.activateDelegate = this.activate.createDelegate(this);
26719
26720     this.addEvents({
26721         /**
26722          * @event tabchange
26723          * Fires when the active tab changes
26724          * @param {Roo.TabPanel} this
26725          * @param {Roo.TabPanelItem} activePanel The new active tab
26726          */
26727         "tabchange": true,
26728         /**
26729          * @event beforetabchange
26730          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26731          * @param {Roo.TabPanel} this
26732          * @param {Object} e Set cancel to true on this object to cancel the tab change
26733          * @param {Roo.TabPanelItem} tab The tab being changed to
26734          */
26735         "beforetabchange" : true
26736     });
26737
26738     Roo.EventManager.onWindowResize(this.onResize, this);
26739     this.cpad = this.el.getPadding("lr");
26740     this.hiddenCount = 0;
26741
26742
26743     // toolbar on the tabbar support...
26744     if (this.toolbar) {
26745         var tcfg = this.toolbar;
26746         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26747         this.toolbar = new Roo.Toolbar(tcfg);
26748         if (Roo.isSafari) {
26749             var tbl = tcfg.container.child('table', true);
26750             tbl.setAttribute('width', '100%');
26751         }
26752         
26753     }
26754    
26755
26756
26757     Roo.TabPanel.superclass.constructor.call(this);
26758 };
26759
26760 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26761     /*
26762      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26763      */
26764     tabPosition : "top",
26765     /*
26766      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26767      */
26768     currentTabWidth : 0,
26769     /*
26770      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26771      */
26772     minTabWidth : 40,
26773     /*
26774      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26775      */
26776     maxTabWidth : 250,
26777     /*
26778      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26779      */
26780     preferredTabWidth : 175,
26781     /*
26782      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26783      */
26784     resizeTabs : false,
26785     /*
26786      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26787      */
26788     monitorResize : true,
26789     /*
26790      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26791      */
26792     toolbar : false,
26793
26794     /**
26795      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26796      * @param {String} id The id of the div to use <b>or create</b>
26797      * @param {String} text The text for the tab
26798      * @param {String} content (optional) Content to put in the TabPanelItem body
26799      * @param {Boolean} closable (optional) True to create a close icon on the tab
26800      * @return {Roo.TabPanelItem} The created TabPanelItem
26801      */
26802     addTab : function(id, text, content, closable){
26803         var item = new Roo.TabPanelItem(this, id, text, closable);
26804         this.addTabItem(item);
26805         if(content){
26806             item.setContent(content);
26807         }
26808         return item;
26809     },
26810
26811     /**
26812      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26813      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26814      * @return {Roo.TabPanelItem}
26815      */
26816     getTab : function(id){
26817         return this.items[id];
26818     },
26819
26820     /**
26821      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26822      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26823      */
26824     hideTab : function(id){
26825         var t = this.items[id];
26826         if(!t.isHidden()){
26827            t.setHidden(true);
26828            this.hiddenCount++;
26829            this.autoSizeTabs();
26830         }
26831     },
26832
26833     /**
26834      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26835      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26836      */
26837     unhideTab : function(id){
26838         var t = this.items[id];
26839         if(t.isHidden()){
26840            t.setHidden(false);
26841            this.hiddenCount--;
26842            this.autoSizeTabs();
26843         }
26844     },
26845
26846     /**
26847      * Adds an existing {@link Roo.TabPanelItem}.
26848      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26849      */
26850     addTabItem : function(item){
26851         this.items[item.id] = item;
26852         this.items.push(item);
26853         if(this.resizeTabs){
26854            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26855            this.autoSizeTabs();
26856         }else{
26857             item.autoSize();
26858         }
26859     },
26860
26861     /**
26862      * Removes a {@link Roo.TabPanelItem}.
26863      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26864      */
26865     removeTab : function(id){
26866         var items = this.items;
26867         var tab = items[id];
26868         if(!tab) { return; }
26869         var index = items.indexOf(tab);
26870         if(this.active == tab && items.length > 1){
26871             var newTab = this.getNextAvailable(index);
26872             if(newTab) {
26873                 newTab.activate();
26874             }
26875         }
26876         this.stripEl.dom.removeChild(tab.pnode.dom);
26877         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26878             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26879         }
26880         items.splice(index, 1);
26881         delete this.items[tab.id];
26882         tab.fireEvent("close", tab);
26883         tab.purgeListeners();
26884         this.autoSizeTabs();
26885     },
26886
26887     getNextAvailable : function(start){
26888         var items = this.items;
26889         var index = start;
26890         // look for a next tab that will slide over to
26891         // replace the one being removed
26892         while(index < items.length){
26893             var item = items[++index];
26894             if(item && !item.isHidden()){
26895                 return item;
26896             }
26897         }
26898         // if one isn't found select the previous tab (on the left)
26899         index = start;
26900         while(index >= 0){
26901             var item = items[--index];
26902             if(item && !item.isHidden()){
26903                 return item;
26904             }
26905         }
26906         return null;
26907     },
26908
26909     /**
26910      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26911      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26912      */
26913     disableTab : function(id){
26914         var tab = this.items[id];
26915         if(tab && this.active != tab){
26916             tab.disable();
26917         }
26918     },
26919
26920     /**
26921      * Enables a {@link Roo.TabPanelItem} that is disabled.
26922      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26923      */
26924     enableTab : function(id){
26925         var tab = this.items[id];
26926         tab.enable();
26927     },
26928
26929     /**
26930      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26931      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26932      * @return {Roo.TabPanelItem} The TabPanelItem.
26933      */
26934     activate : function(id){
26935         var tab = this.items[id];
26936         if(!tab){
26937             return null;
26938         }
26939         if(tab == this.active || tab.disabled){
26940             return tab;
26941         }
26942         var e = {};
26943         this.fireEvent("beforetabchange", this, e, tab);
26944         if(e.cancel !== true && !tab.disabled){
26945             if(this.active){
26946                 this.active.hide();
26947             }
26948             this.active = this.items[id];
26949             this.active.show();
26950             this.fireEvent("tabchange", this, this.active);
26951         }
26952         return tab;
26953     },
26954
26955     /**
26956      * Gets the active {@link Roo.TabPanelItem}.
26957      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26958      */
26959     getActiveTab : function(){
26960         return this.active;
26961     },
26962
26963     /**
26964      * Updates the tab body element to fit the height of the container element
26965      * for overflow scrolling
26966      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26967      */
26968     syncHeight : function(targetHeight){
26969         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26970         var bm = this.bodyEl.getMargins();
26971         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26972         this.bodyEl.setHeight(newHeight);
26973         return newHeight;
26974     },
26975
26976     onResize : function(){
26977         if(this.monitorResize){
26978             this.autoSizeTabs();
26979         }
26980     },
26981
26982     /**
26983      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26984      */
26985     beginUpdate : function(){
26986         this.updating = true;
26987     },
26988
26989     /**
26990      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26991      */
26992     endUpdate : function(){
26993         this.updating = false;
26994         this.autoSizeTabs();
26995     },
26996
26997     /**
26998      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26999      */
27000     autoSizeTabs : function(){
27001         var count = this.items.length;
27002         var vcount = count - this.hiddenCount;
27003         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27004         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27005         var availWidth = Math.floor(w / vcount);
27006         var b = this.stripBody;
27007         if(b.getWidth() > w){
27008             var tabs = this.items;
27009             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27010             if(availWidth < this.minTabWidth){
27011                 /*if(!this.sleft){    // incomplete scrolling code
27012                     this.createScrollButtons();
27013                 }
27014                 this.showScroll();
27015                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27016             }
27017         }else{
27018             if(this.currentTabWidth < this.preferredTabWidth){
27019                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27020             }
27021         }
27022     },
27023
27024     /**
27025      * Returns the number of tabs in this TabPanel.
27026      * @return {Number}
27027      */
27028      getCount : function(){
27029          return this.items.length;
27030      },
27031
27032     /**
27033      * Resizes all the tabs to the passed width
27034      * @param {Number} The new width
27035      */
27036     setTabWidth : function(width){
27037         this.currentTabWidth = width;
27038         for(var i = 0, len = this.items.length; i < len; i++) {
27039                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27040         }
27041     },
27042
27043     /**
27044      * Destroys this TabPanel
27045      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27046      */
27047     destroy : function(removeEl){
27048         Roo.EventManager.removeResizeListener(this.onResize, this);
27049         for(var i = 0, len = this.items.length; i < len; i++){
27050             this.items[i].purgeListeners();
27051         }
27052         if(removeEl === true){
27053             this.el.update("");
27054             this.el.remove();
27055         }
27056     }
27057 });
27058
27059 /**
27060  * @class Roo.TabPanelItem
27061  * @extends Roo.util.Observable
27062  * Represents an individual item (tab plus body) in a TabPanel.
27063  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27064  * @param {String} id The id of this TabPanelItem
27065  * @param {String} text The text for the tab of this TabPanelItem
27066  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27067  */
27068 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27069     /**
27070      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27071      * @type Roo.TabPanel
27072      */
27073     this.tabPanel = tabPanel;
27074     /**
27075      * The id for this TabPanelItem
27076      * @type String
27077      */
27078     this.id = id;
27079     /** @private */
27080     this.disabled = false;
27081     /** @private */
27082     this.text = text;
27083     /** @private */
27084     this.loaded = false;
27085     this.closable = closable;
27086
27087     /**
27088      * The body element for this TabPanelItem.
27089      * @type Roo.Element
27090      */
27091     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27092     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27093     this.bodyEl.setStyle("display", "block");
27094     this.bodyEl.setStyle("zoom", "1");
27095     this.hideAction();
27096
27097     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27098     /** @private */
27099     this.el = Roo.get(els.el, true);
27100     this.inner = Roo.get(els.inner, true);
27101     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27102     this.pnode = Roo.get(els.el.parentNode, true);
27103     this.el.on("mousedown", this.onTabMouseDown, this);
27104     this.el.on("click", this.onTabClick, this);
27105     /** @private */
27106     if(closable){
27107         var c = Roo.get(els.close, true);
27108         c.dom.title = this.closeText;
27109         c.addClassOnOver("close-over");
27110         c.on("click", this.closeClick, this);
27111      }
27112
27113     this.addEvents({
27114          /**
27115          * @event activate
27116          * Fires when this tab becomes the active tab.
27117          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27118          * @param {Roo.TabPanelItem} this
27119          */
27120         "activate": true,
27121         /**
27122          * @event beforeclose
27123          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27124          * @param {Roo.TabPanelItem} this
27125          * @param {Object} e Set cancel to true on this object to cancel the close.
27126          */
27127         "beforeclose": true,
27128         /**
27129          * @event close
27130          * Fires when this tab is closed.
27131          * @param {Roo.TabPanelItem} this
27132          */
27133          "close": true,
27134         /**
27135          * @event deactivate
27136          * Fires when this tab is no longer the active tab.
27137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27138          * @param {Roo.TabPanelItem} this
27139          */
27140          "deactivate" : true
27141     });
27142     this.hidden = false;
27143
27144     Roo.TabPanelItem.superclass.constructor.call(this);
27145 };
27146
27147 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27148     purgeListeners : function(){
27149        Roo.util.Observable.prototype.purgeListeners.call(this);
27150        this.el.removeAllListeners();
27151     },
27152     /**
27153      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27154      */
27155     show : function(){
27156         this.pnode.addClass("on");
27157         this.showAction();
27158         if(Roo.isOpera){
27159             this.tabPanel.stripWrap.repaint();
27160         }
27161         this.fireEvent("activate", this.tabPanel, this);
27162     },
27163
27164     /**
27165      * Returns true if this tab is the active tab.
27166      * @return {Boolean}
27167      */
27168     isActive : function(){
27169         return this.tabPanel.getActiveTab() == this;
27170     },
27171
27172     /**
27173      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27174      */
27175     hide : function(){
27176         this.pnode.removeClass("on");
27177         this.hideAction();
27178         this.fireEvent("deactivate", this.tabPanel, this);
27179     },
27180
27181     hideAction : function(){
27182         this.bodyEl.hide();
27183         this.bodyEl.setStyle("position", "absolute");
27184         this.bodyEl.setLeft("-20000px");
27185         this.bodyEl.setTop("-20000px");
27186     },
27187
27188     showAction : function(){
27189         this.bodyEl.setStyle("position", "relative");
27190         this.bodyEl.setTop("");
27191         this.bodyEl.setLeft("");
27192         this.bodyEl.show();
27193     },
27194
27195     /**
27196      * Set the tooltip for the tab.
27197      * @param {String} tooltip The tab's tooltip
27198      */
27199     setTooltip : function(text){
27200         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27201             this.textEl.dom.qtip = text;
27202             this.textEl.dom.removeAttribute('title');
27203         }else{
27204             this.textEl.dom.title = text;
27205         }
27206     },
27207
27208     onTabClick : function(e){
27209         e.preventDefault();
27210         this.tabPanel.activate(this.id);
27211     },
27212
27213     onTabMouseDown : function(e){
27214         e.preventDefault();
27215         this.tabPanel.activate(this.id);
27216     },
27217
27218     getWidth : function(){
27219         return this.inner.getWidth();
27220     },
27221
27222     setWidth : function(width){
27223         var iwidth = width - this.pnode.getPadding("lr");
27224         this.inner.setWidth(iwidth);
27225         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27226         this.pnode.setWidth(width);
27227     },
27228
27229     /**
27230      * Show or hide the tab
27231      * @param {Boolean} hidden True to hide or false to show.
27232      */
27233     setHidden : function(hidden){
27234         this.hidden = hidden;
27235         this.pnode.setStyle("display", hidden ? "none" : "");
27236     },
27237
27238     /**
27239      * Returns true if this tab is "hidden"
27240      * @return {Boolean}
27241      */
27242     isHidden : function(){
27243         return this.hidden;
27244     },
27245
27246     /**
27247      * Returns the text for this tab
27248      * @return {String}
27249      */
27250     getText : function(){
27251         return this.text;
27252     },
27253
27254     autoSize : function(){
27255         //this.el.beginMeasure();
27256         this.textEl.setWidth(1);
27257         /*
27258          *  #2804 [new] Tabs in Roojs
27259          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27260          */
27261         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27262         //this.el.endMeasure();
27263     },
27264
27265     /**
27266      * Sets the text for the tab (Note: this also sets the tooltip text)
27267      * @param {String} text The tab's text and tooltip
27268      */
27269     setText : function(text){
27270         this.text = text;
27271         this.textEl.update(text);
27272         this.setTooltip(text);
27273         if(!this.tabPanel.resizeTabs){
27274             this.autoSize();
27275         }
27276     },
27277     /**
27278      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27279      */
27280     activate : function(){
27281         this.tabPanel.activate(this.id);
27282     },
27283
27284     /**
27285      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27286      */
27287     disable : function(){
27288         if(this.tabPanel.active != this){
27289             this.disabled = true;
27290             this.pnode.addClass("disabled");
27291         }
27292     },
27293
27294     /**
27295      * Enables this TabPanelItem if it was previously disabled.
27296      */
27297     enable : function(){
27298         this.disabled = false;
27299         this.pnode.removeClass("disabled");
27300     },
27301
27302     /**
27303      * Sets the content for this TabPanelItem.
27304      * @param {String} content The content
27305      * @param {Boolean} loadScripts true to look for and load scripts
27306      */
27307     setContent : function(content, loadScripts){
27308         this.bodyEl.update(content, loadScripts);
27309     },
27310
27311     /**
27312      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27313      * @return {Roo.UpdateManager} The UpdateManager
27314      */
27315     getUpdateManager : function(){
27316         return this.bodyEl.getUpdateManager();
27317     },
27318
27319     /**
27320      * Set a URL to be used to load the content for this TabPanelItem.
27321      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27322      * @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)
27323      * @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)
27324      * @return {Roo.UpdateManager} The UpdateManager
27325      */
27326     setUrl : function(url, params, loadOnce){
27327         if(this.refreshDelegate){
27328             this.un('activate', this.refreshDelegate);
27329         }
27330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27331         this.on("activate", this.refreshDelegate);
27332         return this.bodyEl.getUpdateManager();
27333     },
27334
27335     /** @private */
27336     _handleRefresh : function(url, params, loadOnce){
27337         if(!loadOnce || !this.loaded){
27338             var updater = this.bodyEl.getUpdateManager();
27339             updater.update(url, params, this._setLoaded.createDelegate(this));
27340         }
27341     },
27342
27343     /**
27344      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27345      *   Will fail silently if the setUrl method has not been called.
27346      *   This does not activate the panel, just updates its content.
27347      */
27348     refresh : function(){
27349         if(this.refreshDelegate){
27350            this.loaded = false;
27351            this.refreshDelegate();
27352         }
27353     },
27354
27355     /** @private */
27356     _setLoaded : function(){
27357         this.loaded = true;
27358     },
27359
27360     /** @private */
27361     closeClick : function(e){
27362         var o = {};
27363         e.stopEvent();
27364         this.fireEvent("beforeclose", this, o);
27365         if(o.cancel !== true){
27366             this.tabPanel.removeTab(this.id);
27367         }
27368     },
27369     /**
27370      * The text displayed in the tooltip for the close icon.
27371      * @type String
27372      */
27373     closeText : "Close this tab"
27374 });
27375
27376 /** @private */
27377 Roo.TabPanel.prototype.createStrip = function(container){
27378     var strip = document.createElement("div");
27379     strip.className = "x-tabs-wrap";
27380     container.appendChild(strip);
27381     return strip;
27382 };
27383 /** @private */
27384 Roo.TabPanel.prototype.createStripList = function(strip){
27385     // div wrapper for retard IE
27386     // returns the "tr" element.
27387     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27388         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27389         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27390     return strip.firstChild.firstChild.firstChild.firstChild;
27391 };
27392 /** @private */
27393 Roo.TabPanel.prototype.createBody = function(container){
27394     var body = document.createElement("div");
27395     Roo.id(body, "tab-body");
27396     Roo.fly(body).addClass("x-tabs-body");
27397     container.appendChild(body);
27398     return body;
27399 };
27400 /** @private */
27401 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27402     var body = Roo.getDom(id);
27403     if(!body){
27404         body = document.createElement("div");
27405         body.id = id;
27406     }
27407     Roo.fly(body).addClass("x-tabs-item-body");
27408     bodyEl.insertBefore(body, bodyEl.firstChild);
27409     return body;
27410 };
27411 /** @private */
27412 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27413     var td = document.createElement("td");
27414     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27415     //stripEl.appendChild(td);
27416     if(closable){
27417         td.className = "x-tabs-closable";
27418         if(!this.closeTpl){
27419             this.closeTpl = new Roo.Template(
27420                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27421                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27422                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27423             );
27424         }
27425         var el = this.closeTpl.overwrite(td, {"text": text});
27426         var close = el.getElementsByTagName("div")[0];
27427         var inner = el.getElementsByTagName("em")[0];
27428         return {"el": el, "close": close, "inner": inner};
27429     } else {
27430         if(!this.tabTpl){
27431             this.tabTpl = new Roo.Template(
27432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27434             );
27435         }
27436         var el = this.tabTpl.overwrite(td, {"text": text});
27437         var inner = el.getElementsByTagName("em")[0];
27438         return {"el": el, "inner": inner};
27439     }
27440 };/*
27441  * Based on:
27442  * Ext JS Library 1.1.1
27443  * Copyright(c) 2006-2007, Ext JS, LLC.
27444  *
27445  * Originally Released Under LGPL - original licence link has changed is not relivant.
27446  *
27447  * Fork - LGPL
27448  * <script type="text/javascript">
27449  */
27450
27451 /**
27452  * @class Roo.Button
27453  * @extends Roo.util.Observable
27454  * Simple Button class
27455  * @cfg {String} text The button text
27456  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27457  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27458  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27459  * @cfg {Object} scope The scope of the handler
27460  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27461  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27462  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27463  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27464  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27465  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27466    applies if enableToggle = true)
27467  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27468  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27469   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27470  * @constructor
27471  * Create a new button
27472  * @param {Object} config The config object
27473  */
27474 Roo.Button = function(renderTo, config)
27475 {
27476     if (!config) {
27477         config = renderTo;
27478         renderTo = config.renderTo || false;
27479     }
27480     
27481     Roo.apply(this, config);
27482     this.addEvents({
27483         /**
27484              * @event click
27485              * Fires when this button is clicked
27486              * @param {Button} this
27487              * @param {EventObject} e The click event
27488              */
27489             "click" : true,
27490         /**
27491              * @event toggle
27492              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27493              * @param {Button} this
27494              * @param {Boolean} pressed
27495              */
27496             "toggle" : true,
27497         /**
27498              * @event mouseover
27499              * Fires when the mouse hovers over the button
27500              * @param {Button} this
27501              * @param {Event} e The event object
27502              */
27503         'mouseover' : true,
27504         /**
27505              * @event mouseout
27506              * Fires when the mouse exits the button
27507              * @param {Button} this
27508              * @param {Event} e The event object
27509              */
27510         'mouseout': true,
27511          /**
27512              * @event render
27513              * Fires when the button is rendered
27514              * @param {Button} this
27515              */
27516         'render': true
27517     });
27518     if(this.menu){
27519         this.menu = Roo.menu.MenuMgr.get(this.menu);
27520     }
27521     // register listeners first!!  - so render can be captured..
27522     Roo.util.Observable.call(this);
27523     if(renderTo){
27524         this.render(renderTo);
27525     }
27526     
27527   
27528 };
27529
27530 Roo.extend(Roo.Button, Roo.util.Observable, {
27531     /**
27532      * 
27533      */
27534     
27535     /**
27536      * Read-only. True if this button is hidden
27537      * @type Boolean
27538      */
27539     hidden : false,
27540     /**
27541      * Read-only. True if this button is disabled
27542      * @type Boolean
27543      */
27544     disabled : false,
27545     /**
27546      * Read-only. True if this button is pressed (only if enableToggle = true)
27547      * @type Boolean
27548      */
27549     pressed : false,
27550
27551     /**
27552      * @cfg {Number} tabIndex 
27553      * The DOM tabIndex for this button (defaults to undefined)
27554      */
27555     tabIndex : undefined,
27556
27557     /**
27558      * @cfg {Boolean} enableToggle
27559      * True to enable pressed/not pressed toggling (defaults to false)
27560      */
27561     enableToggle: false,
27562     /**
27563      * @cfg {Mixed} menu
27564      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27565      */
27566     menu : undefined,
27567     /**
27568      * @cfg {String} menuAlign
27569      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27570      */
27571     menuAlign : "tl-bl?",
27572
27573     /**
27574      * @cfg {String} iconCls
27575      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27576      */
27577     iconCls : undefined,
27578     /**
27579      * @cfg {String} type
27580      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27581      */
27582     type : 'button',
27583
27584     // private
27585     menuClassTarget: 'tr',
27586
27587     /**
27588      * @cfg {String} clickEvent
27589      * The type of event to map to the button's event handler (defaults to 'click')
27590      */
27591     clickEvent : 'click',
27592
27593     /**
27594      * @cfg {Boolean} handleMouseEvents
27595      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27596      */
27597     handleMouseEvents : true,
27598
27599     /**
27600      * @cfg {String} tooltipType
27601      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27602      */
27603     tooltipType : 'qtip',
27604
27605     /**
27606      * @cfg {String} cls
27607      * A CSS class to apply to the button's main element.
27608      */
27609     
27610     /**
27611      * @cfg {Roo.Template} template (Optional)
27612      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27613      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27614      * require code modifications if required elements (e.g. a button) aren't present.
27615      */
27616
27617     // private
27618     render : function(renderTo){
27619         var btn;
27620         if(this.hideParent){
27621             this.parentEl = Roo.get(renderTo);
27622         }
27623         if(!this.dhconfig){
27624             if(!this.template){
27625                 if(!Roo.Button.buttonTemplate){
27626                     // hideous table template
27627                     Roo.Button.buttonTemplate = new Roo.Template(
27628                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27629                         '<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>',
27630                         "</tr></tbody></table>");
27631                 }
27632                 this.template = Roo.Button.buttonTemplate;
27633             }
27634             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27635             var btnEl = btn.child("button:first");
27636             btnEl.on('focus', this.onFocus, this);
27637             btnEl.on('blur', this.onBlur, this);
27638             if(this.cls){
27639                 btn.addClass(this.cls);
27640             }
27641             if(this.icon){
27642                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27643             }
27644             if(this.iconCls){
27645                 btnEl.addClass(this.iconCls);
27646                 if(!this.cls){
27647                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27648                 }
27649             }
27650             if(this.tabIndex !== undefined){
27651                 btnEl.dom.tabIndex = this.tabIndex;
27652             }
27653             if(this.tooltip){
27654                 if(typeof this.tooltip == 'object'){
27655                     Roo.QuickTips.tips(Roo.apply({
27656                           target: btnEl.id
27657                     }, this.tooltip));
27658                 } else {
27659                     btnEl.dom[this.tooltipType] = this.tooltip;
27660                 }
27661             }
27662         }else{
27663             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27664         }
27665         this.el = btn;
27666         if(this.id){
27667             this.el.dom.id = this.el.id = this.id;
27668         }
27669         if(this.menu){
27670             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27671             this.menu.on("show", this.onMenuShow, this);
27672             this.menu.on("hide", this.onMenuHide, this);
27673         }
27674         btn.addClass("x-btn");
27675         if(Roo.isIE && !Roo.isIE7){
27676             this.autoWidth.defer(1, this);
27677         }else{
27678             this.autoWidth();
27679         }
27680         if(this.handleMouseEvents){
27681             btn.on("mouseover", this.onMouseOver, this);
27682             btn.on("mouseout", this.onMouseOut, this);
27683             btn.on("mousedown", this.onMouseDown, this);
27684         }
27685         btn.on(this.clickEvent, this.onClick, this);
27686         //btn.on("mouseup", this.onMouseUp, this);
27687         if(this.hidden){
27688             this.hide();
27689         }
27690         if(this.disabled){
27691             this.disable();
27692         }
27693         Roo.ButtonToggleMgr.register(this);
27694         if(this.pressed){
27695             this.el.addClass("x-btn-pressed");
27696         }
27697         if(this.repeat){
27698             var repeater = new Roo.util.ClickRepeater(btn,
27699                 typeof this.repeat == "object" ? this.repeat : {}
27700             );
27701             repeater.on("click", this.onClick,  this);
27702         }
27703         
27704         this.fireEvent('render', this);
27705         
27706     },
27707     /**
27708      * Returns the button's underlying element
27709      * @return {Roo.Element} The element
27710      */
27711     getEl : function(){
27712         return this.el;  
27713     },
27714     
27715     /**
27716      * Destroys this Button and removes any listeners.
27717      */
27718     destroy : function(){
27719         Roo.ButtonToggleMgr.unregister(this);
27720         this.el.removeAllListeners();
27721         this.purgeListeners();
27722         this.el.remove();
27723     },
27724
27725     // private
27726     autoWidth : function(){
27727         if(this.el){
27728             this.el.setWidth("auto");
27729             if(Roo.isIE7 && Roo.isStrict){
27730                 var ib = this.el.child('button');
27731                 if(ib && ib.getWidth() > 20){
27732                     ib.clip();
27733                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27734                 }
27735             }
27736             if(this.minWidth){
27737                 if(this.hidden){
27738                     this.el.beginMeasure();
27739                 }
27740                 if(this.el.getWidth() < this.minWidth){
27741                     this.el.setWidth(this.minWidth);
27742                 }
27743                 if(this.hidden){
27744                     this.el.endMeasure();
27745                 }
27746             }
27747         }
27748     },
27749
27750     /**
27751      * Assigns this button's click handler
27752      * @param {Function} handler The function to call when the button is clicked
27753      * @param {Object} scope (optional) Scope for the function passed in
27754      */
27755     setHandler : function(handler, scope){
27756         this.handler = handler;
27757         this.scope = scope;  
27758     },
27759     
27760     /**
27761      * Sets this button's text
27762      * @param {String} text The button text
27763      */
27764     setText : function(text){
27765         this.text = text;
27766         if(this.el){
27767             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27768         }
27769         this.autoWidth();
27770     },
27771     
27772     /**
27773      * Gets the text for this button
27774      * @return {String} The button text
27775      */
27776     getText : function(){
27777         return this.text;  
27778     },
27779     
27780     /**
27781      * Show this button
27782      */
27783     show: function(){
27784         this.hidden = false;
27785         if(this.el){
27786             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27787         }
27788     },
27789     
27790     /**
27791      * Hide this button
27792      */
27793     hide: function(){
27794         this.hidden = true;
27795         if(this.el){
27796             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27797         }
27798     },
27799     
27800     /**
27801      * Convenience function for boolean show/hide
27802      * @param {Boolean} visible True to show, false to hide
27803      */
27804     setVisible: function(visible){
27805         if(visible) {
27806             this.show();
27807         }else{
27808             this.hide();
27809         }
27810     },
27811     
27812     /**
27813      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27814      * @param {Boolean} state (optional) Force a particular state
27815      */
27816     toggle : function(state){
27817         state = state === undefined ? !this.pressed : state;
27818         if(state != this.pressed){
27819             if(state){
27820                 this.el.addClass("x-btn-pressed");
27821                 this.pressed = true;
27822                 this.fireEvent("toggle", this, true);
27823             }else{
27824                 this.el.removeClass("x-btn-pressed");
27825                 this.pressed = false;
27826                 this.fireEvent("toggle", this, false);
27827             }
27828             if(this.toggleHandler){
27829                 this.toggleHandler.call(this.scope || this, this, state);
27830             }
27831         }
27832     },
27833     
27834     /**
27835      * Focus the button
27836      */
27837     focus : function(){
27838         this.el.child('button:first').focus();
27839     },
27840     
27841     /**
27842      * Disable this button
27843      */
27844     disable : function(){
27845         if(this.el){
27846             this.el.addClass("x-btn-disabled");
27847         }
27848         this.disabled = true;
27849     },
27850     
27851     /**
27852      * Enable this button
27853      */
27854     enable : function(){
27855         if(this.el){
27856             this.el.removeClass("x-btn-disabled");
27857         }
27858         this.disabled = false;
27859     },
27860
27861     /**
27862      * Convenience function for boolean enable/disable
27863      * @param {Boolean} enabled True to enable, false to disable
27864      */
27865     setDisabled : function(v){
27866         this[v !== true ? "enable" : "disable"]();
27867     },
27868
27869     // private
27870     onClick : function(e)
27871     {
27872         if(e){
27873             e.preventDefault();
27874         }
27875         if(e.button != 0){
27876             return;
27877         }
27878         if(!this.disabled){
27879             if(this.enableToggle){
27880                 this.toggle();
27881             }
27882             if(this.menu && !this.menu.isVisible()){
27883                 this.menu.show(this.el, this.menuAlign);
27884             }
27885             this.fireEvent("click", this, e);
27886             if(this.handler){
27887                 this.el.removeClass("x-btn-over");
27888                 this.handler.call(this.scope || this, this, e);
27889             }
27890         }
27891     },
27892     // private
27893     onMouseOver : function(e){
27894         if(!this.disabled){
27895             this.el.addClass("x-btn-over");
27896             this.fireEvent('mouseover', this, e);
27897         }
27898     },
27899     // private
27900     onMouseOut : function(e){
27901         if(!e.within(this.el,  true)){
27902             this.el.removeClass("x-btn-over");
27903             this.fireEvent('mouseout', this, e);
27904         }
27905     },
27906     // private
27907     onFocus : function(e){
27908         if(!this.disabled){
27909             this.el.addClass("x-btn-focus");
27910         }
27911     },
27912     // private
27913     onBlur : function(e){
27914         this.el.removeClass("x-btn-focus");
27915     },
27916     // private
27917     onMouseDown : function(e){
27918         if(!this.disabled && e.button == 0){
27919             this.el.addClass("x-btn-click");
27920             Roo.get(document).on('mouseup', this.onMouseUp, this);
27921         }
27922     },
27923     // private
27924     onMouseUp : function(e){
27925         if(e.button == 0){
27926             this.el.removeClass("x-btn-click");
27927             Roo.get(document).un('mouseup', this.onMouseUp, this);
27928         }
27929     },
27930     // private
27931     onMenuShow : function(e){
27932         this.el.addClass("x-btn-menu-active");
27933     },
27934     // private
27935     onMenuHide : function(e){
27936         this.el.removeClass("x-btn-menu-active");
27937     }   
27938 });
27939
27940 // Private utility class used by Button
27941 Roo.ButtonToggleMgr = function(){
27942    var groups = {};
27943    
27944    function toggleGroup(btn, state){
27945        if(state){
27946            var g = groups[btn.toggleGroup];
27947            for(var i = 0, l = g.length; i < l; i++){
27948                if(g[i] != btn){
27949                    g[i].toggle(false);
27950                }
27951            }
27952        }
27953    }
27954    
27955    return {
27956        register : function(btn){
27957            if(!btn.toggleGroup){
27958                return;
27959            }
27960            var g = groups[btn.toggleGroup];
27961            if(!g){
27962                g = groups[btn.toggleGroup] = [];
27963            }
27964            g.push(btn);
27965            btn.on("toggle", toggleGroup);
27966        },
27967        
27968        unregister : function(btn){
27969            if(!btn.toggleGroup){
27970                return;
27971            }
27972            var g = groups[btn.toggleGroup];
27973            if(g){
27974                g.remove(btn);
27975                btn.un("toggle", toggleGroup);
27976            }
27977        }
27978    };
27979 }();/*
27980  * Based on:
27981  * Ext JS Library 1.1.1
27982  * Copyright(c) 2006-2007, Ext JS, LLC.
27983  *
27984  * Originally Released Under LGPL - original licence link has changed is not relivant.
27985  *
27986  * Fork - LGPL
27987  * <script type="text/javascript">
27988  */
27989  
27990 /**
27991  * @class Roo.SplitButton
27992  * @extends Roo.Button
27993  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27994  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27995  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27996  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27997  * @cfg {String} arrowTooltip The title attribute of the arrow
27998  * @constructor
27999  * Create a new menu button
28000  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28001  * @param {Object} config The config object
28002  */
28003 Roo.SplitButton = function(renderTo, config){
28004     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28005     /**
28006      * @event arrowclick
28007      * Fires when this button's arrow is clicked
28008      * @param {SplitButton} this
28009      * @param {EventObject} e The click event
28010      */
28011     this.addEvents({"arrowclick":true});
28012 };
28013
28014 Roo.extend(Roo.SplitButton, Roo.Button, {
28015     render : function(renderTo){
28016         // this is one sweet looking template!
28017         var tpl = new Roo.Template(
28018             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28019             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28020             '<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>',
28021             "</tbody></table></td><td>",
28022             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28023             '<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>',
28024             "</tbody></table></td></tr></table>"
28025         );
28026         var btn = tpl.append(renderTo, [this.text, this.type], true);
28027         var btnEl = btn.child("button");
28028         if(this.cls){
28029             btn.addClass(this.cls);
28030         }
28031         if(this.icon){
28032             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28033         }
28034         if(this.iconCls){
28035             btnEl.addClass(this.iconCls);
28036             if(!this.cls){
28037                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28038             }
28039         }
28040         this.el = btn;
28041         if(this.handleMouseEvents){
28042             btn.on("mouseover", this.onMouseOver, this);
28043             btn.on("mouseout", this.onMouseOut, this);
28044             btn.on("mousedown", this.onMouseDown, this);
28045             btn.on("mouseup", this.onMouseUp, this);
28046         }
28047         btn.on(this.clickEvent, this.onClick, this);
28048         if(this.tooltip){
28049             if(typeof this.tooltip == 'object'){
28050                 Roo.QuickTips.tips(Roo.apply({
28051                       target: btnEl.id
28052                 }, this.tooltip));
28053             } else {
28054                 btnEl.dom[this.tooltipType] = this.tooltip;
28055             }
28056         }
28057         if(this.arrowTooltip){
28058             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28059         }
28060         if(this.hidden){
28061             this.hide();
28062         }
28063         if(this.disabled){
28064             this.disable();
28065         }
28066         if(this.pressed){
28067             this.el.addClass("x-btn-pressed");
28068         }
28069         if(Roo.isIE && !Roo.isIE7){
28070             this.autoWidth.defer(1, this);
28071         }else{
28072             this.autoWidth();
28073         }
28074         if(this.menu){
28075             this.menu.on("show", this.onMenuShow, this);
28076             this.menu.on("hide", this.onMenuHide, this);
28077         }
28078         this.fireEvent('render', this);
28079     },
28080
28081     // private
28082     autoWidth : function(){
28083         if(this.el){
28084             var tbl = this.el.child("table:first");
28085             var tbl2 = this.el.child("table:last");
28086             this.el.setWidth("auto");
28087             tbl.setWidth("auto");
28088             if(Roo.isIE7 && Roo.isStrict){
28089                 var ib = this.el.child('button:first');
28090                 if(ib && ib.getWidth() > 20){
28091                     ib.clip();
28092                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28093                 }
28094             }
28095             if(this.minWidth){
28096                 if(this.hidden){
28097                     this.el.beginMeasure();
28098                 }
28099                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28100                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28101                 }
28102                 if(this.hidden){
28103                     this.el.endMeasure();
28104                 }
28105             }
28106             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28107         } 
28108     },
28109     /**
28110      * Sets this button's click handler
28111      * @param {Function} handler The function to call when the button is clicked
28112      * @param {Object} scope (optional) Scope for the function passed above
28113      */
28114     setHandler : function(handler, scope){
28115         this.handler = handler;
28116         this.scope = scope;  
28117     },
28118     
28119     /**
28120      * Sets this button's arrow click handler
28121      * @param {Function} handler The function to call when the arrow is clicked
28122      * @param {Object} scope (optional) Scope for the function passed above
28123      */
28124     setArrowHandler : function(handler, scope){
28125         this.arrowHandler = handler;
28126         this.scope = scope;  
28127     },
28128     
28129     /**
28130      * Focus the button
28131      */
28132     focus : function(){
28133         if(this.el){
28134             this.el.child("button:first").focus();
28135         }
28136     },
28137
28138     // private
28139     onClick : function(e){
28140         e.preventDefault();
28141         if(!this.disabled){
28142             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28143                 if(this.menu && !this.menu.isVisible()){
28144                     this.menu.show(this.el, this.menuAlign);
28145                 }
28146                 this.fireEvent("arrowclick", this, e);
28147                 if(this.arrowHandler){
28148                     this.arrowHandler.call(this.scope || this, this, e);
28149                 }
28150             }else{
28151                 this.fireEvent("click", this, e);
28152                 if(this.handler){
28153                     this.handler.call(this.scope || this, this, e);
28154                 }
28155             }
28156         }
28157     },
28158     // private
28159     onMouseDown : function(e){
28160         if(!this.disabled){
28161             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28162         }
28163     },
28164     // private
28165     onMouseUp : function(e){
28166         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28167     }   
28168 });
28169
28170
28171 // backwards compat
28172 Roo.MenuButton = Roo.SplitButton;/*
28173  * Based on:
28174  * Ext JS Library 1.1.1
28175  * Copyright(c) 2006-2007, Ext JS, LLC.
28176  *
28177  * Originally Released Under LGPL - original licence link has changed is not relivant.
28178  *
28179  * Fork - LGPL
28180  * <script type="text/javascript">
28181  */
28182
28183 /**
28184  * @class Roo.Toolbar
28185  * Basic Toolbar class.
28186  * @constructor
28187  * Creates a new Toolbar
28188  * @param {Object} container The config object
28189  */ 
28190 Roo.Toolbar = function(container, buttons, config)
28191 {
28192     /// old consturctor format still supported..
28193     if(container instanceof Array){ // omit the container for later rendering
28194         buttons = container;
28195         config = buttons;
28196         container = null;
28197     }
28198     if (typeof(container) == 'object' && container.xtype) {
28199         config = container;
28200         container = config.container;
28201         buttons = config.buttons || []; // not really - use items!!
28202     }
28203     var xitems = [];
28204     if (config && config.items) {
28205         xitems = config.items;
28206         delete config.items;
28207     }
28208     Roo.apply(this, config);
28209     this.buttons = buttons;
28210     
28211     if(container){
28212         this.render(container);
28213     }
28214     this.xitems = xitems;
28215     Roo.each(xitems, function(b) {
28216         this.add(b);
28217     }, this);
28218     
28219 };
28220
28221 Roo.Toolbar.prototype = {
28222     /**
28223      * @cfg {Array} items
28224      * array of button configs or elements to add (will be converted to a MixedCollection)
28225      */
28226     
28227     /**
28228      * @cfg {String/HTMLElement/Element} container
28229      * The id or element that will contain the toolbar
28230      */
28231     // private
28232     render : function(ct){
28233         this.el = Roo.get(ct);
28234         if(this.cls){
28235             this.el.addClass(this.cls);
28236         }
28237         // using a table allows for vertical alignment
28238         // 100% width is needed by Safari...
28239         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28240         this.tr = this.el.child("tr", true);
28241         var autoId = 0;
28242         this.items = new Roo.util.MixedCollection(false, function(o){
28243             return o.id || ("item" + (++autoId));
28244         });
28245         if(this.buttons){
28246             this.add.apply(this, this.buttons);
28247             delete this.buttons;
28248         }
28249     },
28250
28251     /**
28252      * Adds element(s) to the toolbar -- this function takes a variable number of 
28253      * arguments of mixed type and adds them to the toolbar.
28254      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28255      * <ul>
28256      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28257      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28258      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28259      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28260      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28261      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28262      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28263      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28264      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28265      * </ul>
28266      * @param {Mixed} arg2
28267      * @param {Mixed} etc.
28268      */
28269     add : function(){
28270         var a = arguments, l = a.length;
28271         for(var i = 0; i < l; i++){
28272             this._add(a[i]);
28273         }
28274     },
28275     // private..
28276     _add : function(el) {
28277         
28278         if (el.xtype) {
28279             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28280         }
28281         
28282         if (el.applyTo){ // some kind of form field
28283             return this.addField(el);
28284         } 
28285         if (el.render){ // some kind of Toolbar.Item
28286             return this.addItem(el);
28287         }
28288         if (typeof el == "string"){ // string
28289             if(el == "separator" || el == "-"){
28290                 return this.addSeparator();
28291             }
28292             if (el == " "){
28293                 return this.addSpacer();
28294             }
28295             if(el == "->"){
28296                 return this.addFill();
28297             }
28298             return this.addText(el);
28299             
28300         }
28301         if(el.tagName){ // element
28302             return this.addElement(el);
28303         }
28304         if(typeof el == "object"){ // must be button config?
28305             return this.addButton(el);
28306         }
28307         // and now what?!?!
28308         return false;
28309         
28310     },
28311     
28312     /**
28313      * Add an Xtype element
28314      * @param {Object} xtype Xtype Object
28315      * @return {Object} created Object
28316      */
28317     addxtype : function(e){
28318         return this.add(e);  
28319     },
28320     
28321     /**
28322      * Returns the Element for this toolbar.
28323      * @return {Roo.Element}
28324      */
28325     getEl : function(){
28326         return this.el;  
28327     },
28328     
28329     /**
28330      * Adds a separator
28331      * @return {Roo.Toolbar.Item} The separator item
28332      */
28333     addSeparator : function(){
28334         return this.addItem(new Roo.Toolbar.Separator());
28335     },
28336
28337     /**
28338      * Adds a spacer element
28339      * @return {Roo.Toolbar.Spacer} The spacer item
28340      */
28341     addSpacer : function(){
28342         return this.addItem(new Roo.Toolbar.Spacer());
28343     },
28344
28345     /**
28346      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28347      * @return {Roo.Toolbar.Fill} The fill item
28348      */
28349     addFill : function(){
28350         return this.addItem(new Roo.Toolbar.Fill());
28351     },
28352
28353     /**
28354      * Adds any standard HTML element to the toolbar
28355      * @param {String/HTMLElement/Element} el The element or id of the element to add
28356      * @return {Roo.Toolbar.Item} The element's item
28357      */
28358     addElement : function(el){
28359         return this.addItem(new Roo.Toolbar.Item(el));
28360     },
28361     /**
28362      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28363      * @type Roo.util.MixedCollection  
28364      */
28365     items : false,
28366      
28367     /**
28368      * Adds any Toolbar.Item or subclass
28369      * @param {Roo.Toolbar.Item} item
28370      * @return {Roo.Toolbar.Item} The item
28371      */
28372     addItem : function(item){
28373         var td = this.nextBlock();
28374         item.render(td);
28375         this.items.add(item);
28376         return item;
28377     },
28378     
28379     /**
28380      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28381      * @param {Object/Array} config A button config or array of configs
28382      * @return {Roo.Toolbar.Button/Array}
28383      */
28384     addButton : function(config){
28385         if(config instanceof Array){
28386             var buttons = [];
28387             for(var i = 0, len = config.length; i < len; i++) {
28388                 buttons.push(this.addButton(config[i]));
28389             }
28390             return buttons;
28391         }
28392         var b = config;
28393         if(!(config instanceof Roo.Toolbar.Button)){
28394             b = config.split ?
28395                 new Roo.Toolbar.SplitButton(config) :
28396                 new Roo.Toolbar.Button(config);
28397         }
28398         var td = this.nextBlock();
28399         b.render(td);
28400         this.items.add(b);
28401         return b;
28402     },
28403     
28404     /**
28405      * Adds text to the toolbar
28406      * @param {String} text The text to add
28407      * @return {Roo.Toolbar.Item} The element's item
28408      */
28409     addText : function(text){
28410         return this.addItem(new Roo.Toolbar.TextItem(text));
28411     },
28412     
28413     /**
28414      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28415      * @param {Number} index The index where the item is to be inserted
28416      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28417      * @return {Roo.Toolbar.Button/Item}
28418      */
28419     insertButton : function(index, item){
28420         if(item instanceof Array){
28421             var buttons = [];
28422             for(var i = 0, len = item.length; i < len; i++) {
28423                buttons.push(this.insertButton(index + i, item[i]));
28424             }
28425             return buttons;
28426         }
28427         if (!(item instanceof Roo.Toolbar.Button)){
28428            item = new Roo.Toolbar.Button(item);
28429         }
28430         var td = document.createElement("td");
28431         this.tr.insertBefore(td, this.tr.childNodes[index]);
28432         item.render(td);
28433         this.items.insert(index, item);
28434         return item;
28435     },
28436     
28437     /**
28438      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28439      * @param {Object} config
28440      * @return {Roo.Toolbar.Item} The element's item
28441      */
28442     addDom : function(config, returnEl){
28443         var td = this.nextBlock();
28444         Roo.DomHelper.overwrite(td, config);
28445         var ti = new Roo.Toolbar.Item(td.firstChild);
28446         ti.render(td);
28447         this.items.add(ti);
28448         return ti;
28449     },
28450
28451     /**
28452      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28453      * @type Roo.util.MixedCollection  
28454      */
28455     fields : false,
28456     
28457     /**
28458      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28459      * Note: the field should not have been rendered yet. For a field that has already been
28460      * rendered, use {@link #addElement}.
28461      * @param {Roo.form.Field} field
28462      * @return {Roo.ToolbarItem}
28463      */
28464      
28465       
28466     addField : function(field) {
28467         if (!this.fields) {
28468             var autoId = 0;
28469             this.fields = new Roo.util.MixedCollection(false, function(o){
28470                 return o.id || ("item" + (++autoId));
28471             });
28472
28473         }
28474         
28475         var td = this.nextBlock();
28476         field.render(td);
28477         var ti = new Roo.Toolbar.Item(td.firstChild);
28478         ti.render(td);
28479         this.items.add(ti);
28480         this.fields.add(field);
28481         return ti;
28482     },
28483     /**
28484      * Hide the toolbar
28485      * @method hide
28486      */
28487      
28488       
28489     hide : function()
28490     {
28491         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28492         this.el.child('div').hide();
28493     },
28494     /**
28495      * Show the toolbar
28496      * @method show
28497      */
28498     show : function()
28499     {
28500         this.el.child('div').show();
28501     },
28502       
28503     // private
28504     nextBlock : function(){
28505         var td = document.createElement("td");
28506         this.tr.appendChild(td);
28507         return td;
28508     },
28509
28510     // private
28511     destroy : function(){
28512         if(this.items){ // rendered?
28513             Roo.destroy.apply(Roo, this.items.items);
28514         }
28515         if(this.fields){ // rendered?
28516             Roo.destroy.apply(Roo, this.fields.items);
28517         }
28518         Roo.Element.uncache(this.el, this.tr);
28519     }
28520 };
28521
28522 /**
28523  * @class Roo.Toolbar.Item
28524  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28525  * @constructor
28526  * Creates a new Item
28527  * @param {HTMLElement} el 
28528  */
28529 Roo.Toolbar.Item = function(el){
28530     var cfg = {};
28531     if (typeof (el.xtype) != 'undefined') {
28532         cfg = el;
28533         el = cfg.el;
28534     }
28535     
28536     this.el = Roo.getDom(el);
28537     this.id = Roo.id(this.el);
28538     this.hidden = false;
28539     
28540     this.addEvents({
28541          /**
28542              * @event render
28543              * Fires when the button is rendered
28544              * @param {Button} this
28545              */
28546         'render': true
28547     });
28548     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28549 };
28550 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28551 //Roo.Toolbar.Item.prototype = {
28552     
28553     /**
28554      * Get this item's HTML Element
28555      * @return {HTMLElement}
28556      */
28557     getEl : function(){
28558        return this.el;  
28559     },
28560
28561     // private
28562     render : function(td){
28563         
28564          this.td = td;
28565         td.appendChild(this.el);
28566         
28567         this.fireEvent('render', this);
28568     },
28569     
28570     /**
28571      * Removes and destroys this item.
28572      */
28573     destroy : function(){
28574         this.td.parentNode.removeChild(this.td);
28575     },
28576     
28577     /**
28578      * Shows this item.
28579      */
28580     show: function(){
28581         this.hidden = false;
28582         this.td.style.display = "";
28583     },
28584     
28585     /**
28586      * Hides this item.
28587      */
28588     hide: function(){
28589         this.hidden = true;
28590         this.td.style.display = "none";
28591     },
28592     
28593     /**
28594      * Convenience function for boolean show/hide.
28595      * @param {Boolean} visible true to show/false to hide
28596      */
28597     setVisible: function(visible){
28598         if(visible) {
28599             this.show();
28600         }else{
28601             this.hide();
28602         }
28603     },
28604     
28605     /**
28606      * Try to focus this item.
28607      */
28608     focus : function(){
28609         Roo.fly(this.el).focus();
28610     },
28611     
28612     /**
28613      * Disables this item.
28614      */
28615     disable : function(){
28616         Roo.fly(this.td).addClass("x-item-disabled");
28617         this.disabled = true;
28618         this.el.disabled = true;
28619     },
28620     
28621     /**
28622      * Enables this item.
28623      */
28624     enable : function(){
28625         Roo.fly(this.td).removeClass("x-item-disabled");
28626         this.disabled = false;
28627         this.el.disabled = false;
28628     }
28629 });
28630
28631
28632 /**
28633  * @class Roo.Toolbar.Separator
28634  * @extends Roo.Toolbar.Item
28635  * A simple toolbar separator class
28636  * @constructor
28637  * Creates a new Separator
28638  */
28639 Roo.Toolbar.Separator = function(cfg){
28640     
28641     var s = document.createElement("span");
28642     s.className = "ytb-sep";
28643     if (cfg) {
28644         cfg.el = s;
28645     }
28646     
28647     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28648 };
28649 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28650     enable:Roo.emptyFn,
28651     disable:Roo.emptyFn,
28652     focus:Roo.emptyFn
28653 });
28654
28655 /**
28656  * @class Roo.Toolbar.Spacer
28657  * @extends Roo.Toolbar.Item
28658  * A simple element that adds extra horizontal space to a toolbar.
28659  * @constructor
28660  * Creates a new Spacer
28661  */
28662 Roo.Toolbar.Spacer = function(cfg){
28663     var s = document.createElement("div");
28664     s.className = "ytb-spacer";
28665     if (cfg) {
28666         cfg.el = s;
28667     }
28668     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28669 };
28670 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28671     enable:Roo.emptyFn,
28672     disable:Roo.emptyFn,
28673     focus:Roo.emptyFn
28674 });
28675
28676 /**
28677  * @class Roo.Toolbar.Fill
28678  * @extends Roo.Toolbar.Spacer
28679  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28680  * @constructor
28681  * Creates a new Spacer
28682  */
28683 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28684     // private
28685     render : function(td){
28686         td.style.width = '100%';
28687         Roo.Toolbar.Fill.superclass.render.call(this, td);
28688     }
28689 });
28690
28691 /**
28692  * @class Roo.Toolbar.TextItem
28693  * @extends Roo.Toolbar.Item
28694  * A simple class that renders text directly into a toolbar.
28695  * @constructor
28696  * Creates a new TextItem
28697  * @param {String} text
28698  */
28699 Roo.Toolbar.TextItem = function(cfg){
28700     var  text = cfg || "";
28701     if (typeof(cfg) == 'object') {
28702         text = cfg.text || "";
28703     }  else {
28704         cfg = null;
28705     }
28706     var s = document.createElement("span");
28707     s.className = "ytb-text";
28708     s.innerHTML = text;
28709     if (cfg) {
28710         cfg.el  = s;
28711     }
28712     
28713     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28714 };
28715 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28716     
28717      
28718     enable:Roo.emptyFn,
28719     disable:Roo.emptyFn,
28720     focus:Roo.emptyFn
28721 });
28722
28723 /**
28724  * @class Roo.Toolbar.Button
28725  * @extends Roo.Button
28726  * A button that renders into a toolbar.
28727  * @constructor
28728  * Creates a new Button
28729  * @param {Object} config A standard {@link Roo.Button} config object
28730  */
28731 Roo.Toolbar.Button = function(config){
28732     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28733 };
28734 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28735     render : function(td){
28736         this.td = td;
28737         Roo.Toolbar.Button.superclass.render.call(this, td);
28738     },
28739     
28740     /**
28741      * Removes and destroys this button
28742      */
28743     destroy : function(){
28744         Roo.Toolbar.Button.superclass.destroy.call(this);
28745         this.td.parentNode.removeChild(this.td);
28746     },
28747     
28748     /**
28749      * Shows this button
28750      */
28751     show: function(){
28752         this.hidden = false;
28753         this.td.style.display = "";
28754     },
28755     
28756     /**
28757      * Hides this button
28758      */
28759     hide: function(){
28760         this.hidden = true;
28761         this.td.style.display = "none";
28762     },
28763
28764     /**
28765      * Disables this item
28766      */
28767     disable : function(){
28768         Roo.fly(this.td).addClass("x-item-disabled");
28769         this.disabled = true;
28770     },
28771
28772     /**
28773      * Enables this item
28774      */
28775     enable : function(){
28776         Roo.fly(this.td).removeClass("x-item-disabled");
28777         this.disabled = false;
28778     }
28779 });
28780 // backwards compat
28781 Roo.ToolbarButton = Roo.Toolbar.Button;
28782
28783 /**
28784  * @class Roo.Toolbar.SplitButton
28785  * @extends Roo.SplitButton
28786  * A menu button that renders into a toolbar.
28787  * @constructor
28788  * Creates a new SplitButton
28789  * @param {Object} config A standard {@link Roo.SplitButton} config object
28790  */
28791 Roo.Toolbar.SplitButton = function(config){
28792     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28793 };
28794 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28795     render : function(td){
28796         this.td = td;
28797         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28798     },
28799     
28800     /**
28801      * Removes and destroys this button
28802      */
28803     destroy : function(){
28804         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28805         this.td.parentNode.removeChild(this.td);
28806     },
28807     
28808     /**
28809      * Shows this button
28810      */
28811     show: function(){
28812         this.hidden = false;
28813         this.td.style.display = "";
28814     },
28815     
28816     /**
28817      * Hides this button
28818      */
28819     hide: function(){
28820         this.hidden = true;
28821         this.td.style.display = "none";
28822     }
28823 });
28824
28825 // backwards compat
28826 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28827  * Based on:
28828  * Ext JS Library 1.1.1
28829  * Copyright(c) 2006-2007, Ext JS, LLC.
28830  *
28831  * Originally Released Under LGPL - original licence link has changed is not relivant.
28832  *
28833  * Fork - LGPL
28834  * <script type="text/javascript">
28835  */
28836  
28837 /**
28838  * @class Roo.PagingToolbar
28839  * @extends Roo.Toolbar
28840  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28841  * @constructor
28842  * Create a new PagingToolbar
28843  * @param {Object} config The config object
28844  */
28845 Roo.PagingToolbar = function(el, ds, config)
28846 {
28847     // old args format still supported... - xtype is prefered..
28848     if (typeof(el) == 'object' && el.xtype) {
28849         // created from xtype...
28850         config = el;
28851         ds = el.dataSource;
28852         el = config.container;
28853     }
28854     var items = [];
28855     if (config.items) {
28856         items = config.items;
28857         config.items = [];
28858     }
28859     
28860     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28861     this.ds = ds;
28862     this.cursor = 0;
28863     this.renderButtons(this.el);
28864     this.bind(ds);
28865     
28866     // supprot items array.
28867    
28868     Roo.each(items, function(e) {
28869         this.add(Roo.factory(e));
28870     },this);
28871     
28872 };
28873
28874 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28875     /**
28876      * @cfg {Roo.data.Store} dataSource
28877      * The underlying data store providing the paged data
28878      */
28879     /**
28880      * @cfg {String/HTMLElement/Element} container
28881      * container The id or element that will contain the toolbar
28882      */
28883     /**
28884      * @cfg {Boolean} displayInfo
28885      * True to display the displayMsg (defaults to false)
28886      */
28887     /**
28888      * @cfg {Number} pageSize
28889      * The number of records to display per page (defaults to 20)
28890      */
28891     pageSize: 20,
28892     /**
28893      * @cfg {String} displayMsg
28894      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28895      */
28896     displayMsg : 'Displaying {0} - {1} of {2}',
28897     /**
28898      * @cfg {String} emptyMsg
28899      * The message to display when no records are found (defaults to "No data to display")
28900      */
28901     emptyMsg : 'No data to display',
28902     /**
28903      * Customizable piece of the default paging text (defaults to "Page")
28904      * @type String
28905      */
28906     beforePageText : "Page",
28907     /**
28908      * Customizable piece of the default paging text (defaults to "of %0")
28909      * @type String
28910      */
28911     afterPageText : "of {0}",
28912     /**
28913      * Customizable piece of the default paging text (defaults to "First Page")
28914      * @type String
28915      */
28916     firstText : "First Page",
28917     /**
28918      * Customizable piece of the default paging text (defaults to "Previous Page")
28919      * @type String
28920      */
28921     prevText : "Previous Page",
28922     /**
28923      * Customizable piece of the default paging text (defaults to "Next Page")
28924      * @type String
28925      */
28926     nextText : "Next Page",
28927     /**
28928      * Customizable piece of the default paging text (defaults to "Last Page")
28929      * @type String
28930      */
28931     lastText : "Last Page",
28932     /**
28933      * Customizable piece of the default paging text (defaults to "Refresh")
28934      * @type String
28935      */
28936     refreshText : "Refresh",
28937
28938     // private
28939     renderButtons : function(el){
28940         Roo.PagingToolbar.superclass.render.call(this, el);
28941         this.first = this.addButton({
28942             tooltip: this.firstText,
28943             cls: "x-btn-icon x-grid-page-first",
28944             disabled: true,
28945             handler: this.onClick.createDelegate(this, ["first"])
28946         });
28947         this.prev = this.addButton({
28948             tooltip: this.prevText,
28949             cls: "x-btn-icon x-grid-page-prev",
28950             disabled: true,
28951             handler: this.onClick.createDelegate(this, ["prev"])
28952         });
28953         //this.addSeparator();
28954         this.add(this.beforePageText);
28955         this.field = Roo.get(this.addDom({
28956            tag: "input",
28957            type: "text",
28958            size: "3",
28959            value: "1",
28960            cls: "x-grid-page-number"
28961         }).el);
28962         this.field.on("keydown", this.onPagingKeydown, this);
28963         this.field.on("focus", function(){this.dom.select();});
28964         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28965         this.field.setHeight(18);
28966         //this.addSeparator();
28967         this.next = this.addButton({
28968             tooltip: this.nextText,
28969             cls: "x-btn-icon x-grid-page-next",
28970             disabled: true,
28971             handler: this.onClick.createDelegate(this, ["next"])
28972         });
28973         this.last = this.addButton({
28974             tooltip: this.lastText,
28975             cls: "x-btn-icon x-grid-page-last",
28976             disabled: true,
28977             handler: this.onClick.createDelegate(this, ["last"])
28978         });
28979         //this.addSeparator();
28980         this.loading = this.addButton({
28981             tooltip: this.refreshText,
28982             cls: "x-btn-icon x-grid-loading",
28983             handler: this.onClick.createDelegate(this, ["refresh"])
28984         });
28985
28986         if(this.displayInfo){
28987             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28988         }
28989     },
28990
28991     // private
28992     updateInfo : function(){
28993         if(this.displayEl){
28994             var count = this.ds.getCount();
28995             var msg = count == 0 ?
28996                 this.emptyMsg :
28997                 String.format(
28998                     this.displayMsg,
28999                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29000                 );
29001             this.displayEl.update(msg);
29002         }
29003     },
29004
29005     // private
29006     onLoad : function(ds, r, o){
29007        this.cursor = o.params ? o.params.start : 0;
29008        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29009
29010        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29011        this.field.dom.value = ap;
29012        this.first.setDisabled(ap == 1);
29013        this.prev.setDisabled(ap == 1);
29014        this.next.setDisabled(ap == ps);
29015        this.last.setDisabled(ap == ps);
29016        this.loading.enable();
29017        this.updateInfo();
29018     },
29019
29020     // private
29021     getPageData : function(){
29022         var total = this.ds.getTotalCount();
29023         return {
29024             total : total,
29025             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29026             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29027         };
29028     },
29029
29030     // private
29031     onLoadError : function(){
29032         this.loading.enable();
29033     },
29034
29035     // private
29036     onPagingKeydown : function(e){
29037         var k = e.getKey();
29038         var d = this.getPageData();
29039         if(k == e.RETURN){
29040             var v = this.field.dom.value, pageNum;
29041             if(!v || isNaN(pageNum = parseInt(v, 10))){
29042                 this.field.dom.value = d.activePage;
29043                 return;
29044             }
29045             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29046             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29047             e.stopEvent();
29048         }
29049         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))
29050         {
29051           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29052           this.field.dom.value = pageNum;
29053           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29054           e.stopEvent();
29055         }
29056         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29057         {
29058           var v = this.field.dom.value, pageNum; 
29059           var increment = (e.shiftKey) ? 10 : 1;
29060           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29061             increment *= -1;
29062           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29063             this.field.dom.value = d.activePage;
29064             return;
29065           }
29066           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29067           {
29068             this.field.dom.value = parseInt(v, 10) + increment;
29069             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29070             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29071           }
29072           e.stopEvent();
29073         }
29074     },
29075
29076     // private
29077     beforeLoad : function(){
29078         if(this.loading){
29079             this.loading.disable();
29080         }
29081     },
29082
29083     // private
29084     onClick : function(which){
29085         var ds = this.ds;
29086         switch(which){
29087             case "first":
29088                 ds.load({params:{start: 0, limit: this.pageSize}});
29089             break;
29090             case "prev":
29091                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29092             break;
29093             case "next":
29094                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29095             break;
29096             case "last":
29097                 var total = ds.getTotalCount();
29098                 var extra = total % this.pageSize;
29099                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29100                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29101             break;
29102             case "refresh":
29103                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29104             break;
29105         }
29106     },
29107
29108     /**
29109      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29110      * @param {Roo.data.Store} store The data store to unbind
29111      */
29112     unbind : function(ds){
29113         ds.un("beforeload", this.beforeLoad, this);
29114         ds.un("load", this.onLoad, this);
29115         ds.un("loadexception", this.onLoadError, this);
29116         ds.un("remove", this.updateInfo, this);
29117         ds.un("add", this.updateInfo, this);
29118         this.ds = undefined;
29119     },
29120
29121     /**
29122      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29123      * @param {Roo.data.Store} store The data store to bind
29124      */
29125     bind : function(ds){
29126         ds.on("beforeload", this.beforeLoad, this);
29127         ds.on("load", this.onLoad, this);
29128         ds.on("loadexception", this.onLoadError, this);
29129         ds.on("remove", this.updateInfo, this);
29130         ds.on("add", this.updateInfo, this);
29131         this.ds = ds;
29132     }
29133 });/*
29134  * Based on:
29135  * Ext JS Library 1.1.1
29136  * Copyright(c) 2006-2007, Ext JS, LLC.
29137  *
29138  * Originally Released Under LGPL - original licence link has changed is not relivant.
29139  *
29140  * Fork - LGPL
29141  * <script type="text/javascript">
29142  */
29143
29144 /**
29145  * @class Roo.Resizable
29146  * @extends Roo.util.Observable
29147  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29148  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29149  * 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
29150  * the element will be wrapped for you automatically.</p>
29151  * <p>Here is the list of valid resize handles:</p>
29152  * <pre>
29153 Value   Description
29154 ------  -------------------
29155  'n'     north
29156  's'     south
29157  'e'     east
29158  'w'     west
29159  'nw'    northwest
29160  'sw'    southwest
29161  'se'    southeast
29162  'ne'    northeast
29163  'hd'    horizontal drag
29164  'all'   all
29165 </pre>
29166  * <p>Here's an example showing the creation of a typical Resizable:</p>
29167  * <pre><code>
29168 var resizer = new Roo.Resizable("element-id", {
29169     handles: 'all',
29170     minWidth: 200,
29171     minHeight: 100,
29172     maxWidth: 500,
29173     maxHeight: 400,
29174     pinned: true
29175 });
29176 resizer.on("resize", myHandler);
29177 </code></pre>
29178  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29179  * resizer.east.setDisplayed(false);</p>
29180  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29181  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29182  * resize operation's new size (defaults to [0, 0])
29183  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29184  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29185  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29186  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29187  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29188  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29189  * @cfg {Number} width The width of the element in pixels (defaults to null)
29190  * @cfg {Number} height The height of the element in pixels (defaults to null)
29191  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29192  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29193  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29194  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29195  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29196  * in favor of the handles config option (defaults to false)
29197  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29198  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29199  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29200  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29201  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29202  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29203  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29204  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29205  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29206  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29207  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29208  * @constructor
29209  * Create a new resizable component
29210  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29211  * @param {Object} config configuration options
29212   */
29213 Roo.Resizable = function(el, config)
29214 {
29215     this.el = Roo.get(el);
29216
29217     if(config && config.wrap){
29218         config.resizeChild = this.el;
29219         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29220         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29221         this.el.setStyle("overflow", "hidden");
29222         this.el.setPositioning(config.resizeChild.getPositioning());
29223         config.resizeChild.clearPositioning();
29224         if(!config.width || !config.height){
29225             var csize = config.resizeChild.getSize();
29226             this.el.setSize(csize.width, csize.height);
29227         }
29228         if(config.pinned && !config.adjustments){
29229             config.adjustments = "auto";
29230         }
29231     }
29232
29233     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29234     this.proxy.unselectable();
29235     this.proxy.enableDisplayMode('block');
29236
29237     Roo.apply(this, config);
29238
29239     if(this.pinned){
29240         this.disableTrackOver = true;
29241         this.el.addClass("x-resizable-pinned");
29242     }
29243     // if the element isn't positioned, make it relative
29244     var position = this.el.getStyle("position");
29245     if(position != "absolute" && position != "fixed"){
29246         this.el.setStyle("position", "relative");
29247     }
29248     if(!this.handles){ // no handles passed, must be legacy style
29249         this.handles = 's,e,se';
29250         if(this.multiDirectional){
29251             this.handles += ',n,w';
29252         }
29253     }
29254     if(this.handles == "all"){
29255         this.handles = "n s e w ne nw se sw";
29256     }
29257     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29258     var ps = Roo.Resizable.positions;
29259     for(var i = 0, len = hs.length; i < len; i++){
29260         if(hs[i] && ps[hs[i]]){
29261             var pos = ps[hs[i]];
29262             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29263         }
29264     }
29265     // legacy
29266     this.corner = this.southeast;
29267     
29268     // updateBox = the box can move..
29269     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29270         this.updateBox = true;
29271     }
29272
29273     this.activeHandle = null;
29274
29275     if(this.resizeChild){
29276         if(typeof this.resizeChild == "boolean"){
29277             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29278         }else{
29279             this.resizeChild = Roo.get(this.resizeChild, true);
29280         }
29281     }
29282     
29283     if(this.adjustments == "auto"){
29284         var rc = this.resizeChild;
29285         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29286         if(rc && (hw || hn)){
29287             rc.position("relative");
29288             rc.setLeft(hw ? hw.el.getWidth() : 0);
29289             rc.setTop(hn ? hn.el.getHeight() : 0);
29290         }
29291         this.adjustments = [
29292             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29293             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29294         ];
29295     }
29296
29297     if(this.draggable){
29298         this.dd = this.dynamic ?
29299             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29300         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29301     }
29302
29303     // public events
29304     this.addEvents({
29305         /**
29306          * @event beforeresize
29307          * Fired before resize is allowed. Set enabled to false to cancel resize.
29308          * @param {Roo.Resizable} this
29309          * @param {Roo.EventObject} e The mousedown event
29310          */
29311         "beforeresize" : true,
29312         /**
29313          * @event resizing
29314          * Fired a resizing.
29315          * @param {Roo.Resizable} this
29316          * @param {Number} x The new x position
29317          * @param {Number} y The new y position
29318          * @param {Number} w The new w width
29319          * @param {Number} h The new h hight
29320          * @param {Roo.EventObject} e The mouseup event
29321          */
29322         "resizing" : true,
29323         /**
29324          * @event resize
29325          * Fired after a resize.
29326          * @param {Roo.Resizable} this
29327          * @param {Number} width The new width
29328          * @param {Number} height The new height
29329          * @param {Roo.EventObject} e The mouseup event
29330          */
29331         "resize" : true
29332     });
29333
29334     if(this.width !== null && this.height !== null){
29335         this.resizeTo(this.width, this.height);
29336     }else{
29337         this.updateChildSize();
29338     }
29339     if(Roo.isIE){
29340         this.el.dom.style.zoom = 1;
29341     }
29342     Roo.Resizable.superclass.constructor.call(this);
29343 };
29344
29345 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29346         resizeChild : false,
29347         adjustments : [0, 0],
29348         minWidth : 5,
29349         minHeight : 5,
29350         maxWidth : 10000,
29351         maxHeight : 10000,
29352         enabled : true,
29353         animate : false,
29354         duration : .35,
29355         dynamic : false,
29356         handles : false,
29357         multiDirectional : false,
29358         disableTrackOver : false,
29359         easing : 'easeOutStrong',
29360         widthIncrement : 0,
29361         heightIncrement : 0,
29362         pinned : false,
29363         width : null,
29364         height : null,
29365         preserveRatio : false,
29366         transparent: false,
29367         minX: 0,
29368         minY: 0,
29369         draggable: false,
29370
29371         /**
29372          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29373          */
29374         constrainTo: undefined,
29375         /**
29376          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29377          */
29378         resizeRegion: undefined,
29379
29380
29381     /**
29382      * Perform a manual resize
29383      * @param {Number} width
29384      * @param {Number} height
29385      */
29386     resizeTo : function(width, height){
29387         this.el.setSize(width, height);
29388         this.updateChildSize();
29389         this.fireEvent("resize", this, width, height, null);
29390     },
29391
29392     // private
29393     startSizing : function(e, handle){
29394         this.fireEvent("beforeresize", this, e);
29395         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29396
29397             if(!this.overlay){
29398                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29399                 this.overlay.unselectable();
29400                 this.overlay.enableDisplayMode("block");
29401                 this.overlay.on("mousemove", this.onMouseMove, this);
29402                 this.overlay.on("mouseup", this.onMouseUp, this);
29403             }
29404             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29405
29406             this.resizing = true;
29407             this.startBox = this.el.getBox();
29408             this.startPoint = e.getXY();
29409             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29410                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29411
29412             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29413             this.overlay.show();
29414
29415             if(this.constrainTo) {
29416                 var ct = Roo.get(this.constrainTo);
29417                 this.resizeRegion = ct.getRegion().adjust(
29418                     ct.getFrameWidth('t'),
29419                     ct.getFrameWidth('l'),
29420                     -ct.getFrameWidth('b'),
29421                     -ct.getFrameWidth('r')
29422                 );
29423             }
29424
29425             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29426             this.proxy.show();
29427             this.proxy.setBox(this.startBox);
29428             if(!this.dynamic){
29429                 this.proxy.setStyle('visibility', 'visible');
29430             }
29431         }
29432     },
29433
29434     // private
29435     onMouseDown : function(handle, e){
29436         if(this.enabled){
29437             e.stopEvent();
29438             this.activeHandle = handle;
29439             this.startSizing(e, handle);
29440         }
29441     },
29442
29443     // private
29444     onMouseUp : function(e){
29445         var size = this.resizeElement();
29446         this.resizing = false;
29447         this.handleOut();
29448         this.overlay.hide();
29449         this.proxy.hide();
29450         this.fireEvent("resize", this, size.width, size.height, e);
29451     },
29452
29453     // private
29454     updateChildSize : function(){
29455         
29456         if(this.resizeChild){
29457             var el = this.el;
29458             var child = this.resizeChild;
29459             var adj = this.adjustments;
29460             if(el.dom.offsetWidth){
29461                 var b = el.getSize(true);
29462                 child.setSize(b.width+adj[0], b.height+adj[1]);
29463             }
29464             // Second call here for IE
29465             // The first call enables instant resizing and
29466             // the second call corrects scroll bars if they
29467             // exist
29468             if(Roo.isIE){
29469                 setTimeout(function(){
29470                     if(el.dom.offsetWidth){
29471                         var b = el.getSize(true);
29472                         child.setSize(b.width+adj[0], b.height+adj[1]);
29473                     }
29474                 }, 10);
29475             }
29476         }
29477     },
29478
29479     // private
29480     snap : function(value, inc, min){
29481         if(!inc || !value) return value;
29482         var newValue = value;
29483         var m = value % inc;
29484         if(m > 0){
29485             if(m > (inc/2)){
29486                 newValue = value + (inc-m);
29487             }else{
29488                 newValue = value - m;
29489             }
29490         }
29491         return Math.max(min, newValue);
29492     },
29493
29494     // private
29495     resizeElement : function(){
29496         var box = this.proxy.getBox();
29497         if(this.updateBox){
29498             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29499         }else{
29500             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29501         }
29502         this.updateChildSize();
29503         if(!this.dynamic){
29504             this.proxy.hide();
29505         }
29506         return box;
29507     },
29508
29509     // private
29510     constrain : function(v, diff, m, mx){
29511         if(v - diff < m){
29512             diff = v - m;
29513         }else if(v - diff > mx){
29514             diff = mx - v;
29515         }
29516         return diff;
29517     },
29518
29519     // private
29520     onMouseMove : function(e){
29521         
29522         if(this.enabled){
29523             try{// try catch so if something goes wrong the user doesn't get hung
29524
29525             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29526                 return;
29527             }
29528
29529             //var curXY = this.startPoint;
29530             var curSize = this.curSize || this.startBox;
29531             var x = this.startBox.x, y = this.startBox.y;
29532             var ox = x, oy = y;
29533             var w = curSize.width, h = curSize.height;
29534             var ow = w, oh = h;
29535             var mw = this.minWidth, mh = this.minHeight;
29536             var mxw = this.maxWidth, mxh = this.maxHeight;
29537             var wi = this.widthIncrement;
29538             var hi = this.heightIncrement;
29539
29540             var eventXY = e.getXY();
29541             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29542             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29543
29544             var pos = this.activeHandle.position;
29545
29546             switch(pos){
29547                 case "east":
29548                     w += diffX;
29549                     w = Math.min(Math.max(mw, w), mxw);
29550                     break;
29551              
29552                 case "south":
29553                     h += diffY;
29554                     h = Math.min(Math.max(mh, h), mxh);
29555                     break;
29556                 case "southeast":
29557                     w += diffX;
29558                     h += diffY;
29559                     w = Math.min(Math.max(mw, w), mxw);
29560                     h = Math.min(Math.max(mh, h), mxh);
29561                     break;
29562                 case "north":
29563                     diffY = this.constrain(h, diffY, mh, mxh);
29564                     y += diffY;
29565                     h -= diffY;
29566                     break;
29567                 case "hdrag":
29568                     
29569                     if (wi) {
29570                         var adiffX = Math.abs(diffX);
29571                         var sub = (adiffX % wi); // how much 
29572                         if (sub > (wi/2)) { // far enough to snap
29573                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29574                         } else {
29575                             // remove difference.. 
29576                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29577                         }
29578                     }
29579                     x += diffX;
29580                     x = Math.max(this.minX, x);
29581                     break;
29582                 case "west":
29583                     diffX = this.constrain(w, diffX, mw, mxw);
29584                     x += diffX;
29585                     w -= diffX;
29586                     break;
29587                 case "northeast":
29588                     w += diffX;
29589                     w = Math.min(Math.max(mw, w), mxw);
29590                     diffY = this.constrain(h, diffY, mh, mxh);
29591                     y += diffY;
29592                     h -= diffY;
29593                     break;
29594                 case "northwest":
29595                     diffX = this.constrain(w, diffX, mw, mxw);
29596                     diffY = this.constrain(h, diffY, mh, mxh);
29597                     y += diffY;
29598                     h -= diffY;
29599                     x += diffX;
29600                     w -= diffX;
29601                     break;
29602                case "southwest":
29603                     diffX = this.constrain(w, diffX, mw, mxw);
29604                     h += diffY;
29605                     h = Math.min(Math.max(mh, h), mxh);
29606                     x += diffX;
29607                     w -= diffX;
29608                     break;
29609             }
29610
29611             var sw = this.snap(w, wi, mw);
29612             var sh = this.snap(h, hi, mh);
29613             if(sw != w || sh != h){
29614                 switch(pos){
29615                     case "northeast":
29616                         y -= sh - h;
29617                     break;
29618                     case "north":
29619                         y -= sh - h;
29620                         break;
29621                     case "southwest":
29622                         x -= sw - w;
29623                     break;
29624                     case "west":
29625                         x -= sw - w;
29626                         break;
29627                     case "northwest":
29628                         x -= sw - w;
29629                         y -= sh - h;
29630                     break;
29631                 }
29632                 w = sw;
29633                 h = sh;
29634             }
29635
29636             if(this.preserveRatio){
29637                 switch(pos){
29638                     case "southeast":
29639                     case "east":
29640                         h = oh * (w/ow);
29641                         h = Math.min(Math.max(mh, h), mxh);
29642                         w = ow * (h/oh);
29643                        break;
29644                     case "south":
29645                         w = ow * (h/oh);
29646                         w = Math.min(Math.max(mw, w), mxw);
29647                         h = oh * (w/ow);
29648                         break;
29649                     case "northeast":
29650                         w = ow * (h/oh);
29651                         w = Math.min(Math.max(mw, w), mxw);
29652                         h = oh * (w/ow);
29653                     break;
29654                     case "north":
29655                         var tw = w;
29656                         w = ow * (h/oh);
29657                         w = Math.min(Math.max(mw, w), mxw);
29658                         h = oh * (w/ow);
29659                         x += (tw - w) / 2;
29660                         break;
29661                     case "southwest":
29662                         h = oh * (w/ow);
29663                         h = Math.min(Math.max(mh, h), mxh);
29664                         var tw = w;
29665                         w = ow * (h/oh);
29666                         x += tw - w;
29667                         break;
29668                     case "west":
29669                         var th = h;
29670                         h = oh * (w/ow);
29671                         h = Math.min(Math.max(mh, h), mxh);
29672                         y += (th - h) / 2;
29673                         var tw = w;
29674                         w = ow * (h/oh);
29675                         x += tw - w;
29676                        break;
29677                     case "northwest":
29678                         var tw = w;
29679                         var th = h;
29680                         h = oh * (w/ow);
29681                         h = Math.min(Math.max(mh, h), mxh);
29682                         w = ow * (h/oh);
29683                         y += th - h;
29684                         x += tw - w;
29685                        break;
29686
29687                 }
29688             }
29689             if (pos == 'hdrag') {
29690                 w = ow;
29691             }
29692             this.proxy.setBounds(x, y, w, h);
29693             if(this.dynamic){
29694                 this.resizeElement();
29695             }
29696             }catch(e){}
29697         }
29698         this.fireEvent("resizing", this, x, y, w, h, e);
29699     },
29700
29701     // private
29702     handleOver : function(){
29703         if(this.enabled){
29704             this.el.addClass("x-resizable-over");
29705         }
29706     },
29707
29708     // private
29709     handleOut : function(){
29710         if(!this.resizing){
29711             this.el.removeClass("x-resizable-over");
29712         }
29713     },
29714
29715     /**
29716      * Returns the element this component is bound to.
29717      * @return {Roo.Element}
29718      */
29719     getEl : function(){
29720         return this.el;
29721     },
29722
29723     /**
29724      * Returns the resizeChild element (or null).
29725      * @return {Roo.Element}
29726      */
29727     getResizeChild : function(){
29728         return this.resizeChild;
29729     },
29730     groupHandler : function()
29731     {
29732         
29733     },
29734     /**
29735      * Destroys this resizable. If the element was wrapped and
29736      * removeEl is not true then the element remains.
29737      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29738      */
29739     destroy : function(removeEl){
29740         this.proxy.remove();
29741         if(this.overlay){
29742             this.overlay.removeAllListeners();
29743             this.overlay.remove();
29744         }
29745         var ps = Roo.Resizable.positions;
29746         for(var k in ps){
29747             if(typeof ps[k] != "function" && this[ps[k]]){
29748                 var h = this[ps[k]];
29749                 h.el.removeAllListeners();
29750                 h.el.remove();
29751             }
29752         }
29753         if(removeEl){
29754             this.el.update("");
29755             this.el.remove();
29756         }
29757     }
29758 });
29759
29760 // private
29761 // hash to map config positions to true positions
29762 Roo.Resizable.positions = {
29763     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29764     hd: "hdrag"
29765 };
29766
29767 // private
29768 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29769     if(!this.tpl){
29770         // only initialize the template if resizable is used
29771         var tpl = Roo.DomHelper.createTemplate(
29772             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29773         );
29774         tpl.compile();
29775         Roo.Resizable.Handle.prototype.tpl = tpl;
29776     }
29777     this.position = pos;
29778     this.rz = rz;
29779     // show north drag fro topdra
29780     var handlepos = pos == 'hdrag' ? 'north' : pos;
29781     
29782     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29783     if (pos == 'hdrag') {
29784         this.el.setStyle('cursor', 'pointer');
29785     }
29786     this.el.unselectable();
29787     if(transparent){
29788         this.el.setOpacity(0);
29789     }
29790     this.el.on("mousedown", this.onMouseDown, this);
29791     if(!disableTrackOver){
29792         this.el.on("mouseover", this.onMouseOver, this);
29793         this.el.on("mouseout", this.onMouseOut, this);
29794     }
29795 };
29796
29797 // private
29798 Roo.Resizable.Handle.prototype = {
29799     afterResize : function(rz){
29800         Roo.log('after?');
29801         // do nothing
29802     },
29803     // private
29804     onMouseDown : function(e){
29805         this.rz.onMouseDown(this, e);
29806     },
29807     // private
29808     onMouseOver : function(e){
29809         this.rz.handleOver(this, e);
29810     },
29811     // private
29812     onMouseOut : function(e){
29813         this.rz.handleOut(this, e);
29814     }
29815 };/*
29816  * Based on:
29817  * Ext JS Library 1.1.1
29818  * Copyright(c) 2006-2007, Ext JS, LLC.
29819  *
29820  * Originally Released Under LGPL - original licence link has changed is not relivant.
29821  *
29822  * Fork - LGPL
29823  * <script type="text/javascript">
29824  */
29825
29826 /**
29827  * @class Roo.Editor
29828  * @extends Roo.Component
29829  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29830  * @constructor
29831  * Create a new Editor
29832  * @param {Roo.form.Field} field The Field object (or descendant)
29833  * @param {Object} config The config object
29834  */
29835 Roo.Editor = function(field, config){
29836     Roo.Editor.superclass.constructor.call(this, config);
29837     this.field = field;
29838     this.addEvents({
29839         /**
29840              * @event beforestartedit
29841              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29842              * false from the handler of this event.
29843              * @param {Editor} this
29844              * @param {Roo.Element} boundEl The underlying element bound to this editor
29845              * @param {Mixed} value The field value being set
29846              */
29847         "beforestartedit" : true,
29848         /**
29849              * @event startedit
29850              * Fires when this editor is displayed
29851              * @param {Roo.Element} boundEl The underlying element bound to this editor
29852              * @param {Mixed} value The starting field value
29853              */
29854         "startedit" : true,
29855         /**
29856              * @event beforecomplete
29857              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29858              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29859              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29860              * event will not fire since no edit actually occurred.
29861              * @param {Editor} this
29862              * @param {Mixed} value The current field value
29863              * @param {Mixed} startValue The original field value
29864              */
29865         "beforecomplete" : true,
29866         /**
29867              * @event complete
29868              * Fires after editing is complete and any changed value has been written to the underlying field.
29869              * @param {Editor} this
29870              * @param {Mixed} value The current field value
29871              * @param {Mixed} startValue The original field value
29872              */
29873         "complete" : true,
29874         /**
29875          * @event specialkey
29876          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29877          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29878          * @param {Roo.form.Field} this
29879          * @param {Roo.EventObject} e The event object
29880          */
29881         "specialkey" : true
29882     });
29883 };
29884
29885 Roo.extend(Roo.Editor, Roo.Component, {
29886     /**
29887      * @cfg {Boolean/String} autosize
29888      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29889      * or "height" to adopt the height only (defaults to false)
29890      */
29891     /**
29892      * @cfg {Boolean} revertInvalid
29893      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29894      * validation fails (defaults to true)
29895      */
29896     /**
29897      * @cfg {Boolean} ignoreNoChange
29898      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29899      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29900      * will never be ignored.
29901      */
29902     /**
29903      * @cfg {Boolean} hideEl
29904      * False to keep the bound element visible while the editor is displayed (defaults to true)
29905      */
29906     /**
29907      * @cfg {Mixed} value
29908      * The data value of the underlying field (defaults to "")
29909      */
29910     value : "",
29911     /**
29912      * @cfg {String} alignment
29913      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29914      */
29915     alignment: "c-c?",
29916     /**
29917      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29918      * for bottom-right shadow (defaults to "frame")
29919      */
29920     shadow : "frame",
29921     /**
29922      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29923      */
29924     constrain : false,
29925     /**
29926      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29927      */
29928     completeOnEnter : false,
29929     /**
29930      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29931      */
29932     cancelOnEsc : false,
29933     /**
29934      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29935      */
29936     updateEl : false,
29937
29938     // private
29939     onRender : function(ct, position){
29940         this.el = new Roo.Layer({
29941             shadow: this.shadow,
29942             cls: "x-editor",
29943             parentEl : ct,
29944             shim : this.shim,
29945             shadowOffset:4,
29946             id: this.id,
29947             constrain: this.constrain
29948         });
29949         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29950         if(this.field.msgTarget != 'title'){
29951             this.field.msgTarget = 'qtip';
29952         }
29953         this.field.render(this.el);
29954         if(Roo.isGecko){
29955             this.field.el.dom.setAttribute('autocomplete', 'off');
29956         }
29957         this.field.on("specialkey", this.onSpecialKey, this);
29958         if(this.swallowKeys){
29959             this.field.el.swallowEvent(['keydown','keypress']);
29960         }
29961         this.field.show();
29962         this.field.on("blur", this.onBlur, this);
29963         if(this.field.grow){
29964             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29965         }
29966     },
29967
29968     onSpecialKey : function(field, e)
29969     {
29970         //Roo.log('editor onSpecialKey');
29971         if(this.completeOnEnter && e.getKey() == e.ENTER){
29972             e.stopEvent();
29973             this.completeEdit();
29974             return;
29975         }
29976         // do not fire special key otherwise it might hide close the editor...
29977         if(e.getKey() == e.ENTER){    
29978             return;
29979         }
29980         if(this.cancelOnEsc && e.getKey() == e.ESC){
29981             this.cancelEdit();
29982             return;
29983         } 
29984         this.fireEvent('specialkey', field, e);
29985     
29986     },
29987
29988     /**
29989      * Starts the editing process and shows the editor.
29990      * @param {String/HTMLElement/Element} el The element to edit
29991      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29992       * to the innerHTML of el.
29993      */
29994     startEdit : function(el, value){
29995         if(this.editing){
29996             this.completeEdit();
29997         }
29998         this.boundEl = Roo.get(el);
29999         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30000         if(!this.rendered){
30001             this.render(this.parentEl || document.body);
30002         }
30003         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30004             return;
30005         }
30006         this.startValue = v;
30007         this.field.setValue(v);
30008         if(this.autoSize){
30009             var sz = this.boundEl.getSize();
30010             switch(this.autoSize){
30011                 case "width":
30012                 this.setSize(sz.width,  "");
30013                 break;
30014                 case "height":
30015                 this.setSize("",  sz.height);
30016                 break;
30017                 default:
30018                 this.setSize(sz.width,  sz.height);
30019             }
30020         }
30021         this.el.alignTo(this.boundEl, this.alignment);
30022         this.editing = true;
30023         if(Roo.QuickTips){
30024             Roo.QuickTips.disable();
30025         }
30026         this.show();
30027     },
30028
30029     /**
30030      * Sets the height and width of this editor.
30031      * @param {Number} width The new width
30032      * @param {Number} height The new height
30033      */
30034     setSize : function(w, h){
30035         this.field.setSize(w, h);
30036         if(this.el){
30037             this.el.sync();
30038         }
30039     },
30040
30041     /**
30042      * Realigns the editor to the bound field based on the current alignment config value.
30043      */
30044     realign : function(){
30045         this.el.alignTo(this.boundEl, this.alignment);
30046     },
30047
30048     /**
30049      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30050      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30051      */
30052     completeEdit : function(remainVisible){
30053         if(!this.editing){
30054             return;
30055         }
30056         var v = this.getValue();
30057         if(this.revertInvalid !== false && !this.field.isValid()){
30058             v = this.startValue;
30059             this.cancelEdit(true);
30060         }
30061         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30062             this.editing = false;
30063             this.hide();
30064             return;
30065         }
30066         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30067             this.editing = false;
30068             if(this.updateEl && this.boundEl){
30069                 this.boundEl.update(v);
30070             }
30071             if(remainVisible !== true){
30072                 this.hide();
30073             }
30074             this.fireEvent("complete", this, v, this.startValue);
30075         }
30076     },
30077
30078     // private
30079     onShow : function(){
30080         this.el.show();
30081         if(this.hideEl !== false){
30082             this.boundEl.hide();
30083         }
30084         this.field.show();
30085         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30086             this.fixIEFocus = true;
30087             this.deferredFocus.defer(50, this);
30088         }else{
30089             this.field.focus();
30090         }
30091         this.fireEvent("startedit", this.boundEl, this.startValue);
30092     },
30093
30094     deferredFocus : function(){
30095         if(this.editing){
30096             this.field.focus();
30097         }
30098     },
30099
30100     /**
30101      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30102      * reverted to the original starting value.
30103      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30104      * cancel (defaults to false)
30105      */
30106     cancelEdit : function(remainVisible){
30107         if(this.editing){
30108             this.setValue(this.startValue);
30109             if(remainVisible !== true){
30110                 this.hide();
30111             }
30112         }
30113     },
30114
30115     // private
30116     onBlur : function(){
30117         if(this.allowBlur !== true && this.editing){
30118             this.completeEdit();
30119         }
30120     },
30121
30122     // private
30123     onHide : function(){
30124         if(this.editing){
30125             this.completeEdit();
30126             return;
30127         }
30128         this.field.blur();
30129         if(this.field.collapse){
30130             this.field.collapse();
30131         }
30132         this.el.hide();
30133         if(this.hideEl !== false){
30134             this.boundEl.show();
30135         }
30136         if(Roo.QuickTips){
30137             Roo.QuickTips.enable();
30138         }
30139     },
30140
30141     /**
30142      * Sets the data value of the editor
30143      * @param {Mixed} value Any valid value supported by the underlying field
30144      */
30145     setValue : function(v){
30146         this.field.setValue(v);
30147     },
30148
30149     /**
30150      * Gets the data value of the editor
30151      * @return {Mixed} The data value
30152      */
30153     getValue : function(){
30154         return this.field.getValue();
30155     }
30156 });/*
30157  * Based on:
30158  * Ext JS Library 1.1.1
30159  * Copyright(c) 2006-2007, Ext JS, LLC.
30160  *
30161  * Originally Released Under LGPL - original licence link has changed is not relivant.
30162  *
30163  * Fork - LGPL
30164  * <script type="text/javascript">
30165  */
30166  
30167 /**
30168  * @class Roo.BasicDialog
30169  * @extends Roo.util.Observable
30170  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30171  * <pre><code>
30172 var dlg = new Roo.BasicDialog("my-dlg", {
30173     height: 200,
30174     width: 300,
30175     minHeight: 100,
30176     minWidth: 150,
30177     modal: true,
30178     proxyDrag: true,
30179     shadow: true
30180 });
30181 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30182 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30183 dlg.addButton('Cancel', dlg.hide, dlg);
30184 dlg.show();
30185 </code></pre>
30186   <b>A Dialog should always be a direct child of the body element.</b>
30187  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30188  * @cfg {String} title Default text to display in the title bar (defaults to null)
30189  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30190  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30191  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30192  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30193  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30194  * (defaults to null with no animation)
30195  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30196  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30197  * property for valid values (defaults to 'all')
30198  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30199  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30200  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30201  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30202  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30203  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30204  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30205  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30206  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30207  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30208  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30209  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30210  * draggable = true (defaults to false)
30211  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30212  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30213  * shadow (defaults to false)
30214  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30215  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30216  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30217  * @cfg {Array} buttons Array of buttons
30218  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30219  * @constructor
30220  * Create a new BasicDialog.
30221  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30222  * @param {Object} config Configuration options
30223  */
30224 Roo.BasicDialog = function(el, config){
30225     this.el = Roo.get(el);
30226     var dh = Roo.DomHelper;
30227     if(!this.el && config && config.autoCreate){
30228         if(typeof config.autoCreate == "object"){
30229             if(!config.autoCreate.id){
30230                 config.autoCreate.id = el;
30231             }
30232             this.el = dh.append(document.body,
30233                         config.autoCreate, true);
30234         }else{
30235             this.el = dh.append(document.body,
30236                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30237         }
30238     }
30239     el = this.el;
30240     el.setDisplayed(true);
30241     el.hide = this.hideAction;
30242     this.id = el.id;
30243     el.addClass("x-dlg");
30244
30245     Roo.apply(this, config);
30246
30247     this.proxy = el.createProxy("x-dlg-proxy");
30248     this.proxy.hide = this.hideAction;
30249     this.proxy.setOpacity(.5);
30250     this.proxy.hide();
30251
30252     if(config.width){
30253         el.setWidth(config.width);
30254     }
30255     if(config.height){
30256         el.setHeight(config.height);
30257     }
30258     this.size = el.getSize();
30259     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30260         this.xy = [config.x,config.y];
30261     }else{
30262         this.xy = el.getCenterXY(true);
30263     }
30264     /** The header element @type Roo.Element */
30265     this.header = el.child("> .x-dlg-hd");
30266     /** The body element @type Roo.Element */
30267     this.body = el.child("> .x-dlg-bd");
30268     /** The footer element @type Roo.Element */
30269     this.footer = el.child("> .x-dlg-ft");
30270
30271     if(!this.header){
30272         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30273     }
30274     if(!this.body){
30275         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30276     }
30277
30278     this.header.unselectable();
30279     if(this.title){
30280         this.header.update(this.title);
30281     }
30282     // this element allows the dialog to be focused for keyboard event
30283     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30284     this.focusEl.swallowEvent("click", true);
30285
30286     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30287
30288     // wrap the body and footer for special rendering
30289     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30290     if(this.footer){
30291         this.bwrap.dom.appendChild(this.footer.dom);
30292     }
30293
30294     this.bg = this.el.createChild({
30295         tag: "div", cls:"x-dlg-bg",
30296         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30297     });
30298     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30299
30300
30301     if(this.autoScroll !== false && !this.autoTabs){
30302         this.body.setStyle("overflow", "auto");
30303     }
30304
30305     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30306
30307     if(this.closable !== false){
30308         this.el.addClass("x-dlg-closable");
30309         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30310         this.close.on("click", this.closeClick, this);
30311         this.close.addClassOnOver("x-dlg-close-over");
30312     }
30313     if(this.collapsible !== false){
30314         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30315         this.collapseBtn.on("click", this.collapseClick, this);
30316         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30317         this.header.on("dblclick", this.collapseClick, this);
30318     }
30319     if(this.resizable !== false){
30320         this.el.addClass("x-dlg-resizable");
30321         this.resizer = new Roo.Resizable(el, {
30322             minWidth: this.minWidth || 80,
30323             minHeight:this.minHeight || 80,
30324             handles: this.resizeHandles || "all",
30325             pinned: true
30326         });
30327         this.resizer.on("beforeresize", this.beforeResize, this);
30328         this.resizer.on("resize", this.onResize, this);
30329     }
30330     if(this.draggable !== false){
30331         el.addClass("x-dlg-draggable");
30332         if (!this.proxyDrag) {
30333             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30334         }
30335         else {
30336             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30337         }
30338         dd.setHandleElId(this.header.id);
30339         dd.endDrag = this.endMove.createDelegate(this);
30340         dd.startDrag = this.startMove.createDelegate(this);
30341         dd.onDrag = this.onDrag.createDelegate(this);
30342         dd.scroll = false;
30343         this.dd = dd;
30344     }
30345     if(this.modal){
30346         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30347         this.mask.enableDisplayMode("block");
30348         this.mask.hide();
30349         this.el.addClass("x-dlg-modal");
30350     }
30351     if(this.shadow){
30352         this.shadow = new Roo.Shadow({
30353             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30354             offset : this.shadowOffset
30355         });
30356     }else{
30357         this.shadowOffset = 0;
30358     }
30359     if(Roo.useShims && this.shim !== false){
30360         this.shim = this.el.createShim();
30361         this.shim.hide = this.hideAction;
30362         this.shim.hide();
30363     }else{
30364         this.shim = false;
30365     }
30366     if(this.autoTabs){
30367         this.initTabs();
30368     }
30369     if (this.buttons) { 
30370         var bts= this.buttons;
30371         this.buttons = [];
30372         Roo.each(bts, function(b) {
30373             this.addButton(b);
30374         }, this);
30375     }
30376     
30377     
30378     this.addEvents({
30379         /**
30380          * @event keydown
30381          * Fires when a key is pressed
30382          * @param {Roo.BasicDialog} this
30383          * @param {Roo.EventObject} e
30384          */
30385         "keydown" : true,
30386         /**
30387          * @event move
30388          * Fires when this dialog is moved by the user.
30389          * @param {Roo.BasicDialog} this
30390          * @param {Number} x The new page X
30391          * @param {Number} y The new page Y
30392          */
30393         "move" : true,
30394         /**
30395          * @event resize
30396          * Fires when this dialog is resized by the user.
30397          * @param {Roo.BasicDialog} this
30398          * @param {Number} width The new width
30399          * @param {Number} height The new height
30400          */
30401         "resize" : true,
30402         /**
30403          * @event beforehide
30404          * Fires before this dialog is hidden.
30405          * @param {Roo.BasicDialog} this
30406          */
30407         "beforehide" : true,
30408         /**
30409          * @event hide
30410          * Fires when this dialog is hidden.
30411          * @param {Roo.BasicDialog} this
30412          */
30413         "hide" : true,
30414         /**
30415          * @event beforeshow
30416          * Fires before this dialog is shown.
30417          * @param {Roo.BasicDialog} this
30418          */
30419         "beforeshow" : true,
30420         /**
30421          * @event show
30422          * Fires when this dialog is shown.
30423          * @param {Roo.BasicDialog} this
30424          */
30425         "show" : true
30426     });
30427     el.on("keydown", this.onKeyDown, this);
30428     el.on("mousedown", this.toFront, this);
30429     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30430     this.el.hide();
30431     Roo.DialogManager.register(this);
30432     Roo.BasicDialog.superclass.constructor.call(this);
30433 };
30434
30435 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30436     shadowOffset: Roo.isIE ? 6 : 5,
30437     minHeight: 80,
30438     minWidth: 200,
30439     minButtonWidth: 75,
30440     defaultButton: null,
30441     buttonAlign: "right",
30442     tabTag: 'div',
30443     firstShow: true,
30444
30445     /**
30446      * Sets the dialog title text
30447      * @param {String} text The title text to display
30448      * @return {Roo.BasicDialog} this
30449      */
30450     setTitle : function(text){
30451         this.header.update(text);
30452         return this;
30453     },
30454
30455     // private
30456     closeClick : function(){
30457         this.hide();
30458     },
30459
30460     // private
30461     collapseClick : function(){
30462         this[this.collapsed ? "expand" : "collapse"]();
30463     },
30464
30465     /**
30466      * Collapses the dialog to its minimized state (only the title bar is visible).
30467      * Equivalent to the user clicking the collapse dialog button.
30468      */
30469     collapse : function(){
30470         if(!this.collapsed){
30471             this.collapsed = true;
30472             this.el.addClass("x-dlg-collapsed");
30473             this.restoreHeight = this.el.getHeight();
30474             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30475         }
30476     },
30477
30478     /**
30479      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30480      * clicking the expand dialog button.
30481      */
30482     expand : function(){
30483         if(this.collapsed){
30484             this.collapsed = false;
30485             this.el.removeClass("x-dlg-collapsed");
30486             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30487         }
30488     },
30489
30490     /**
30491      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30492      * @return {Roo.TabPanel} The tabs component
30493      */
30494     initTabs : function(){
30495         var tabs = this.getTabs();
30496         while(tabs.getTab(0)){
30497             tabs.removeTab(0);
30498         }
30499         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30500             var dom = el.dom;
30501             tabs.addTab(Roo.id(dom), dom.title);
30502             dom.title = "";
30503         });
30504         tabs.activate(0);
30505         return tabs;
30506     },
30507
30508     // private
30509     beforeResize : function(){
30510         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30511     },
30512
30513     // private
30514     onResize : function(){
30515         this.refreshSize();
30516         this.syncBodyHeight();
30517         this.adjustAssets();
30518         this.focus();
30519         this.fireEvent("resize", this, this.size.width, this.size.height);
30520     },
30521
30522     // private
30523     onKeyDown : function(e){
30524         if(this.isVisible()){
30525             this.fireEvent("keydown", this, e);
30526         }
30527     },
30528
30529     /**
30530      * Resizes the dialog.
30531      * @param {Number} width
30532      * @param {Number} height
30533      * @return {Roo.BasicDialog} this
30534      */
30535     resizeTo : function(width, height){
30536         this.el.setSize(width, height);
30537         this.size = {width: width, height: height};
30538         this.syncBodyHeight();
30539         if(this.fixedcenter){
30540             this.center();
30541         }
30542         if(this.isVisible()){
30543             this.constrainXY();
30544             this.adjustAssets();
30545         }
30546         this.fireEvent("resize", this, width, height);
30547         return this;
30548     },
30549
30550
30551     /**
30552      * Resizes the dialog to fit the specified content size.
30553      * @param {Number} width
30554      * @param {Number} height
30555      * @return {Roo.BasicDialog} this
30556      */
30557     setContentSize : function(w, h){
30558         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30559         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30560         //if(!this.el.isBorderBox()){
30561             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30562             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30563         //}
30564         if(this.tabs){
30565             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30566             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30567         }
30568         this.resizeTo(w, h);
30569         return this;
30570     },
30571
30572     /**
30573      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30574      * executed in response to a particular key being pressed while the dialog is active.
30575      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30576      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30577      * @param {Function} fn The function to call
30578      * @param {Object} scope (optional) The scope of the function
30579      * @return {Roo.BasicDialog} this
30580      */
30581     addKeyListener : function(key, fn, scope){
30582         var keyCode, shift, ctrl, alt;
30583         if(typeof key == "object" && !(key instanceof Array)){
30584             keyCode = key["key"];
30585             shift = key["shift"];
30586             ctrl = key["ctrl"];
30587             alt = key["alt"];
30588         }else{
30589             keyCode = key;
30590         }
30591         var handler = function(dlg, e){
30592             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30593                 var k = e.getKey();
30594                 if(keyCode instanceof Array){
30595                     for(var i = 0, len = keyCode.length; i < len; i++){
30596                         if(keyCode[i] == k){
30597                           fn.call(scope || window, dlg, k, e);
30598                           return;
30599                         }
30600                     }
30601                 }else{
30602                     if(k == keyCode){
30603                         fn.call(scope || window, dlg, k, e);
30604                     }
30605                 }
30606             }
30607         };
30608         this.on("keydown", handler);
30609         return this;
30610     },
30611
30612     /**
30613      * Returns the TabPanel component (creates it if it doesn't exist).
30614      * Note: If you wish to simply check for the existence of tabs without creating them,
30615      * check for a null 'tabs' property.
30616      * @return {Roo.TabPanel} The tabs component
30617      */
30618     getTabs : function(){
30619         if(!this.tabs){
30620             this.el.addClass("x-dlg-auto-tabs");
30621             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30622             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30623         }
30624         return this.tabs;
30625     },
30626
30627     /**
30628      * Adds a button to the footer section of the dialog.
30629      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30630      * object or a valid Roo.DomHelper element config
30631      * @param {Function} handler The function called when the button is clicked
30632      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30633      * @return {Roo.Button} The new button
30634      */
30635     addButton : function(config, handler, scope){
30636         var dh = Roo.DomHelper;
30637         if(!this.footer){
30638             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30639         }
30640         if(!this.btnContainer){
30641             var tb = this.footer.createChild({
30642
30643                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30644                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30645             }, null, true);
30646             this.btnContainer = tb.firstChild.firstChild.firstChild;
30647         }
30648         var bconfig = {
30649             handler: handler,
30650             scope: scope,
30651             minWidth: this.minButtonWidth,
30652             hideParent:true
30653         };
30654         if(typeof config == "string"){
30655             bconfig.text = config;
30656         }else{
30657             if(config.tag){
30658                 bconfig.dhconfig = config;
30659             }else{
30660                 Roo.apply(bconfig, config);
30661             }
30662         }
30663         var fc = false;
30664         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30665             bconfig.position = Math.max(0, bconfig.position);
30666             fc = this.btnContainer.childNodes[bconfig.position];
30667         }
30668          
30669         var btn = new Roo.Button(
30670             fc ? 
30671                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30672                 : this.btnContainer.appendChild(document.createElement("td")),
30673             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30674             bconfig
30675         );
30676         this.syncBodyHeight();
30677         if(!this.buttons){
30678             /**
30679              * Array of all the buttons that have been added to this dialog via addButton
30680              * @type Array
30681              */
30682             this.buttons = [];
30683         }
30684         this.buttons.push(btn);
30685         return btn;
30686     },
30687
30688     /**
30689      * Sets the default button to be focused when the dialog is displayed.
30690      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30691      * @return {Roo.BasicDialog} this
30692      */
30693     setDefaultButton : function(btn){
30694         this.defaultButton = btn;
30695         return this;
30696     },
30697
30698     // private
30699     getHeaderFooterHeight : function(safe){
30700         var height = 0;
30701         if(this.header){
30702            height += this.header.getHeight();
30703         }
30704         if(this.footer){
30705            var fm = this.footer.getMargins();
30706             height += (this.footer.getHeight()+fm.top+fm.bottom);
30707         }
30708         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30709         height += this.centerBg.getPadding("tb");
30710         return height;
30711     },
30712
30713     // private
30714     syncBodyHeight : function()
30715     {
30716         var bd = this.body, // the text
30717             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30718             bw = this.bwrap;
30719         var height = this.size.height - this.getHeaderFooterHeight(false);
30720         bd.setHeight(height-bd.getMargins("tb"));
30721         var hh = this.header.getHeight();
30722         var h = this.size.height-hh;
30723         cb.setHeight(h);
30724         
30725         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30726         bw.setHeight(h-cb.getPadding("tb"));
30727         
30728         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30729         bd.setWidth(bw.getWidth(true));
30730         if(this.tabs){
30731             this.tabs.syncHeight();
30732             if(Roo.isIE){
30733                 this.tabs.el.repaint();
30734             }
30735         }
30736     },
30737
30738     /**
30739      * Restores the previous state of the dialog if Roo.state is configured.
30740      * @return {Roo.BasicDialog} this
30741      */
30742     restoreState : function(){
30743         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30744         if(box && box.width){
30745             this.xy = [box.x, box.y];
30746             this.resizeTo(box.width, box.height);
30747         }
30748         return this;
30749     },
30750
30751     // private
30752     beforeShow : function(){
30753         this.expand();
30754         if(this.fixedcenter){
30755             this.xy = this.el.getCenterXY(true);
30756         }
30757         if(this.modal){
30758             Roo.get(document.body).addClass("x-body-masked");
30759             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30760             this.mask.show();
30761         }
30762         this.constrainXY();
30763     },
30764
30765     // private
30766     animShow : function(){
30767         var b = Roo.get(this.animateTarget).getBox();
30768         this.proxy.setSize(b.width, b.height);
30769         this.proxy.setLocation(b.x, b.y);
30770         this.proxy.show();
30771         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30772                     true, .35, this.showEl.createDelegate(this));
30773     },
30774
30775     /**
30776      * Shows the dialog.
30777      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30778      * @return {Roo.BasicDialog} this
30779      */
30780     show : function(animateTarget){
30781         if (this.fireEvent("beforeshow", this) === false){
30782             return;
30783         }
30784         if(this.syncHeightBeforeShow){
30785             this.syncBodyHeight();
30786         }else if(this.firstShow){
30787             this.firstShow = false;
30788             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30789         }
30790         this.animateTarget = animateTarget || this.animateTarget;
30791         if(!this.el.isVisible()){
30792             this.beforeShow();
30793             if(this.animateTarget && Roo.get(this.animateTarget)){
30794                 this.animShow();
30795             }else{
30796                 this.showEl();
30797             }
30798         }
30799         return this;
30800     },
30801
30802     // private
30803     showEl : function(){
30804         this.proxy.hide();
30805         this.el.setXY(this.xy);
30806         this.el.show();
30807         this.adjustAssets(true);
30808         this.toFront();
30809         this.focus();
30810         // IE peekaboo bug - fix found by Dave Fenwick
30811         if(Roo.isIE){
30812             this.el.repaint();
30813         }
30814         this.fireEvent("show", this);
30815     },
30816
30817     /**
30818      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30819      * dialog itself will receive focus.
30820      */
30821     focus : function(){
30822         if(this.defaultButton){
30823             this.defaultButton.focus();
30824         }else{
30825             this.focusEl.focus();
30826         }
30827     },
30828
30829     // private
30830     constrainXY : function(){
30831         if(this.constraintoviewport !== false){
30832             if(!this.viewSize){
30833                 if(this.container){
30834                     var s = this.container.getSize();
30835                     this.viewSize = [s.width, s.height];
30836                 }else{
30837                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30838                 }
30839             }
30840             var s = Roo.get(this.container||document).getScroll();
30841
30842             var x = this.xy[0], y = this.xy[1];
30843             var w = this.size.width, h = this.size.height;
30844             var vw = this.viewSize[0], vh = this.viewSize[1];
30845             // only move it if it needs it
30846             var moved = false;
30847             // first validate right/bottom
30848             if(x + w > vw+s.left){
30849                 x = vw - w;
30850                 moved = true;
30851             }
30852             if(y + h > vh+s.top){
30853                 y = vh - h;
30854                 moved = true;
30855             }
30856             // then make sure top/left isn't negative
30857             if(x < s.left){
30858                 x = s.left;
30859                 moved = true;
30860             }
30861             if(y < s.top){
30862                 y = s.top;
30863                 moved = true;
30864             }
30865             if(moved){
30866                 // cache xy
30867                 this.xy = [x, y];
30868                 if(this.isVisible()){
30869                     this.el.setLocation(x, y);
30870                     this.adjustAssets();
30871                 }
30872             }
30873         }
30874     },
30875
30876     // private
30877     onDrag : function(){
30878         if(!this.proxyDrag){
30879             this.xy = this.el.getXY();
30880             this.adjustAssets();
30881         }
30882     },
30883
30884     // private
30885     adjustAssets : function(doShow){
30886         var x = this.xy[0], y = this.xy[1];
30887         var w = this.size.width, h = this.size.height;
30888         if(doShow === true){
30889             if(this.shadow){
30890                 this.shadow.show(this.el);
30891             }
30892             if(this.shim){
30893                 this.shim.show();
30894             }
30895         }
30896         if(this.shadow && this.shadow.isVisible()){
30897             this.shadow.show(this.el);
30898         }
30899         if(this.shim && this.shim.isVisible()){
30900             this.shim.setBounds(x, y, w, h);
30901         }
30902     },
30903
30904     // private
30905     adjustViewport : function(w, h){
30906         if(!w || !h){
30907             w = Roo.lib.Dom.getViewWidth();
30908             h = Roo.lib.Dom.getViewHeight();
30909         }
30910         // cache the size
30911         this.viewSize = [w, h];
30912         if(this.modal && this.mask.isVisible()){
30913             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30914             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30915         }
30916         if(this.isVisible()){
30917             this.constrainXY();
30918         }
30919     },
30920
30921     /**
30922      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30923      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30924      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30925      */
30926     destroy : function(removeEl){
30927         if(this.isVisible()){
30928             this.animateTarget = null;
30929             this.hide();
30930         }
30931         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30932         if(this.tabs){
30933             this.tabs.destroy(removeEl);
30934         }
30935         Roo.destroy(
30936              this.shim,
30937              this.proxy,
30938              this.resizer,
30939              this.close,
30940              this.mask
30941         );
30942         if(this.dd){
30943             this.dd.unreg();
30944         }
30945         if(this.buttons){
30946            for(var i = 0, len = this.buttons.length; i < len; i++){
30947                this.buttons[i].destroy();
30948            }
30949         }
30950         this.el.removeAllListeners();
30951         if(removeEl === true){
30952             this.el.update("");
30953             this.el.remove();
30954         }
30955         Roo.DialogManager.unregister(this);
30956     },
30957
30958     // private
30959     startMove : function(){
30960         if(this.proxyDrag){
30961             this.proxy.show();
30962         }
30963         if(this.constraintoviewport !== false){
30964             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30965         }
30966     },
30967
30968     // private
30969     endMove : function(){
30970         if(!this.proxyDrag){
30971             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30972         }else{
30973             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30974             this.proxy.hide();
30975         }
30976         this.refreshSize();
30977         this.adjustAssets();
30978         this.focus();
30979         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30980     },
30981
30982     /**
30983      * Brings this dialog to the front of any other visible dialogs
30984      * @return {Roo.BasicDialog} this
30985      */
30986     toFront : function(){
30987         Roo.DialogManager.bringToFront(this);
30988         return this;
30989     },
30990
30991     /**
30992      * Sends this dialog to the back (under) of any other visible dialogs
30993      * @return {Roo.BasicDialog} this
30994      */
30995     toBack : function(){
30996         Roo.DialogManager.sendToBack(this);
30997         return this;
30998     },
30999
31000     /**
31001      * Centers this dialog in the viewport
31002      * @return {Roo.BasicDialog} this
31003      */
31004     center : function(){
31005         var xy = this.el.getCenterXY(true);
31006         this.moveTo(xy[0], xy[1]);
31007         return this;
31008     },
31009
31010     /**
31011      * Moves the dialog's top-left corner to the specified point
31012      * @param {Number} x
31013      * @param {Number} y
31014      * @return {Roo.BasicDialog} this
31015      */
31016     moveTo : function(x, y){
31017         this.xy = [x,y];
31018         if(this.isVisible()){
31019             this.el.setXY(this.xy);
31020             this.adjustAssets();
31021         }
31022         return this;
31023     },
31024
31025     /**
31026      * Aligns the dialog to the specified element
31027      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31028      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31029      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31030      * @return {Roo.BasicDialog} this
31031      */
31032     alignTo : function(element, position, offsets){
31033         this.xy = this.el.getAlignToXY(element, position, offsets);
31034         if(this.isVisible()){
31035             this.el.setXY(this.xy);
31036             this.adjustAssets();
31037         }
31038         return this;
31039     },
31040
31041     /**
31042      * Anchors an element to another element and realigns it when the window is resized.
31043      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31044      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31045      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31046      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31047      * is a number, it is used as the buffer delay (defaults to 50ms).
31048      * @return {Roo.BasicDialog} this
31049      */
31050     anchorTo : function(el, alignment, offsets, monitorScroll){
31051         var action = function(){
31052             this.alignTo(el, alignment, offsets);
31053         };
31054         Roo.EventManager.onWindowResize(action, this);
31055         var tm = typeof monitorScroll;
31056         if(tm != 'undefined'){
31057             Roo.EventManager.on(window, 'scroll', action, this,
31058                 {buffer: tm == 'number' ? monitorScroll : 50});
31059         }
31060         action.call(this);
31061         return this;
31062     },
31063
31064     /**
31065      * Returns true if the dialog is visible
31066      * @return {Boolean}
31067      */
31068     isVisible : function(){
31069         return this.el.isVisible();
31070     },
31071
31072     // private
31073     animHide : function(callback){
31074         var b = Roo.get(this.animateTarget).getBox();
31075         this.proxy.show();
31076         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31077         this.el.hide();
31078         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31079                     this.hideEl.createDelegate(this, [callback]));
31080     },
31081
31082     /**
31083      * Hides the dialog.
31084      * @param {Function} callback (optional) Function to call when the dialog is hidden
31085      * @return {Roo.BasicDialog} this
31086      */
31087     hide : function(callback){
31088         if (this.fireEvent("beforehide", this) === false){
31089             return;
31090         }
31091         if(this.shadow){
31092             this.shadow.hide();
31093         }
31094         if(this.shim) {
31095           this.shim.hide();
31096         }
31097         // sometimes animateTarget seems to get set.. causing problems...
31098         // this just double checks..
31099         if(this.animateTarget && Roo.get(this.animateTarget)) {
31100            this.animHide(callback);
31101         }else{
31102             this.el.hide();
31103             this.hideEl(callback);
31104         }
31105         return this;
31106     },
31107
31108     // private
31109     hideEl : function(callback){
31110         this.proxy.hide();
31111         if(this.modal){
31112             this.mask.hide();
31113             Roo.get(document.body).removeClass("x-body-masked");
31114         }
31115         this.fireEvent("hide", this);
31116         if(typeof callback == "function"){
31117             callback();
31118         }
31119     },
31120
31121     // private
31122     hideAction : function(){
31123         this.setLeft("-10000px");
31124         this.setTop("-10000px");
31125         this.setStyle("visibility", "hidden");
31126     },
31127
31128     // private
31129     refreshSize : function(){
31130         this.size = this.el.getSize();
31131         this.xy = this.el.getXY();
31132         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31133     },
31134
31135     // private
31136     // z-index is managed by the DialogManager and may be overwritten at any time
31137     setZIndex : function(index){
31138         if(this.modal){
31139             this.mask.setStyle("z-index", index);
31140         }
31141         if(this.shim){
31142             this.shim.setStyle("z-index", ++index);
31143         }
31144         if(this.shadow){
31145             this.shadow.setZIndex(++index);
31146         }
31147         this.el.setStyle("z-index", ++index);
31148         if(this.proxy){
31149             this.proxy.setStyle("z-index", ++index);
31150         }
31151         if(this.resizer){
31152             this.resizer.proxy.setStyle("z-index", ++index);
31153         }
31154
31155         this.lastZIndex = index;
31156     },
31157
31158     /**
31159      * Returns the element for this dialog
31160      * @return {Roo.Element} The underlying dialog Element
31161      */
31162     getEl : function(){
31163         return this.el;
31164     }
31165 });
31166
31167 /**
31168  * @class Roo.DialogManager
31169  * Provides global access to BasicDialogs that have been created and
31170  * support for z-indexing (layering) multiple open dialogs.
31171  */
31172 Roo.DialogManager = function(){
31173     var list = {};
31174     var accessList = [];
31175     var front = null;
31176
31177     // private
31178     var sortDialogs = function(d1, d2){
31179         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31180     };
31181
31182     // private
31183     var orderDialogs = function(){
31184         accessList.sort(sortDialogs);
31185         var seed = Roo.DialogManager.zseed;
31186         for(var i = 0, len = accessList.length; i < len; i++){
31187             var dlg = accessList[i];
31188             if(dlg){
31189                 dlg.setZIndex(seed + (i*10));
31190             }
31191         }
31192     };
31193
31194     return {
31195         /**
31196          * The starting z-index for BasicDialogs (defaults to 9000)
31197          * @type Number The z-index value
31198          */
31199         zseed : 9000,
31200
31201         // private
31202         register : function(dlg){
31203             list[dlg.id] = dlg;
31204             accessList.push(dlg);
31205         },
31206
31207         // private
31208         unregister : function(dlg){
31209             delete list[dlg.id];
31210             var i=0;
31211             var len=0;
31212             if(!accessList.indexOf){
31213                 for(  i = 0, len = accessList.length; i < len; i++){
31214                     if(accessList[i] == dlg){
31215                         accessList.splice(i, 1);
31216                         return;
31217                     }
31218                 }
31219             }else{
31220                  i = accessList.indexOf(dlg);
31221                 if(i != -1){
31222                     accessList.splice(i, 1);
31223                 }
31224             }
31225         },
31226
31227         /**
31228          * Gets a registered dialog by id
31229          * @param {String/Object} id The id of the dialog or a dialog
31230          * @return {Roo.BasicDialog} this
31231          */
31232         get : function(id){
31233             return typeof id == "object" ? id : list[id];
31234         },
31235
31236         /**
31237          * Brings the specified dialog to the front
31238          * @param {String/Object} dlg The id of the dialog or a dialog
31239          * @return {Roo.BasicDialog} this
31240          */
31241         bringToFront : function(dlg){
31242             dlg = this.get(dlg);
31243             if(dlg != front){
31244                 front = dlg;
31245                 dlg._lastAccess = new Date().getTime();
31246                 orderDialogs();
31247             }
31248             return dlg;
31249         },
31250
31251         /**
31252          * Sends the specified dialog to the back
31253          * @param {String/Object} dlg The id of the dialog or a dialog
31254          * @return {Roo.BasicDialog} this
31255          */
31256         sendToBack : function(dlg){
31257             dlg = this.get(dlg);
31258             dlg._lastAccess = -(new Date().getTime());
31259             orderDialogs();
31260             return dlg;
31261         },
31262
31263         /**
31264          * Hides all dialogs
31265          */
31266         hideAll : function(){
31267             for(var id in list){
31268                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31269                     list[id].hide();
31270                 }
31271             }
31272         }
31273     };
31274 }();
31275
31276 /**
31277  * @class Roo.LayoutDialog
31278  * @extends Roo.BasicDialog
31279  * Dialog which provides adjustments for working with a layout in a Dialog.
31280  * Add your necessary layout config options to the dialog's config.<br>
31281  * Example usage (including a nested layout):
31282  * <pre><code>
31283 if(!dialog){
31284     dialog = new Roo.LayoutDialog("download-dlg", {
31285         modal: true,
31286         width:600,
31287         height:450,
31288         shadow:true,
31289         minWidth:500,
31290         minHeight:350,
31291         autoTabs:true,
31292         proxyDrag:true,
31293         // layout config merges with the dialog config
31294         center:{
31295             tabPosition: "top",
31296             alwaysShowTabs: true
31297         }
31298     });
31299     dialog.addKeyListener(27, dialog.hide, dialog);
31300     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31301     dialog.addButton("Build It!", this.getDownload, this);
31302
31303     // we can even add nested layouts
31304     var innerLayout = new Roo.BorderLayout("dl-inner", {
31305         east: {
31306             initialSize: 200,
31307             autoScroll:true,
31308             split:true
31309         },
31310         center: {
31311             autoScroll:true
31312         }
31313     });
31314     innerLayout.beginUpdate();
31315     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31316     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31317     innerLayout.endUpdate(true);
31318
31319     var layout = dialog.getLayout();
31320     layout.beginUpdate();
31321     layout.add("center", new Roo.ContentPanel("standard-panel",
31322                         {title: "Download the Source", fitToFrame:true}));
31323     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31324                {title: "Build your own roo.js"}));
31325     layout.getRegion("center").showPanel(sp);
31326     layout.endUpdate();
31327 }
31328 </code></pre>
31329     * @constructor
31330     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31331     * @param {Object} config configuration options
31332   */
31333 Roo.LayoutDialog = function(el, cfg){
31334     
31335     var config=  cfg;
31336     if (typeof(cfg) == 'undefined') {
31337         config = Roo.apply({}, el);
31338         // not sure why we use documentElement here.. - it should always be body.
31339         // IE7 borks horribly if we use documentElement.
31340         // webkit also does not like documentElement - it creates a body element...
31341         el = Roo.get( document.body || document.documentElement ).createChild();
31342         //config.autoCreate = true;
31343     }
31344     
31345     
31346     config.autoTabs = false;
31347     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31348     this.body.setStyle({overflow:"hidden", position:"relative"});
31349     this.layout = new Roo.BorderLayout(this.body.dom, config);
31350     this.layout.monitorWindowResize = false;
31351     this.el.addClass("x-dlg-auto-layout");
31352     // fix case when center region overwrites center function
31353     this.center = Roo.BasicDialog.prototype.center;
31354     this.on("show", this.layout.layout, this.layout, true);
31355     if (config.items) {
31356         var xitems = config.items;
31357         delete config.items;
31358         Roo.each(xitems, this.addxtype, this);
31359     }
31360     
31361     
31362 };
31363 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31364     /**
31365      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31366      * @deprecated
31367      */
31368     endUpdate : function(){
31369         this.layout.endUpdate();
31370     },
31371
31372     /**
31373      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31374      *  @deprecated
31375      */
31376     beginUpdate : function(){
31377         this.layout.beginUpdate();
31378     },
31379
31380     /**
31381      * Get the BorderLayout for this dialog
31382      * @return {Roo.BorderLayout}
31383      */
31384     getLayout : function(){
31385         return this.layout;
31386     },
31387
31388     showEl : function(){
31389         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31390         if(Roo.isIE7){
31391             this.layout.layout();
31392         }
31393     },
31394
31395     // private
31396     // Use the syncHeightBeforeShow config option to control this automatically
31397     syncBodyHeight : function(){
31398         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31399         if(this.layout){this.layout.layout();}
31400     },
31401     
31402       /**
31403      * Add an xtype element (actually adds to the layout.)
31404      * @return {Object} xdata xtype object data.
31405      */
31406     
31407     addxtype : function(c) {
31408         return this.layout.addxtype(c);
31409     }
31410 });/*
31411  * Based on:
31412  * Ext JS Library 1.1.1
31413  * Copyright(c) 2006-2007, Ext JS, LLC.
31414  *
31415  * Originally Released Under LGPL - original licence link has changed is not relivant.
31416  *
31417  * Fork - LGPL
31418  * <script type="text/javascript">
31419  */
31420  
31421 /**
31422  * @class Roo.MessageBox
31423  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31424  * Example usage:
31425  *<pre><code>
31426 // Basic alert:
31427 Roo.Msg.alert('Status', 'Changes saved successfully.');
31428
31429 // Prompt for user data:
31430 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31431     if (btn == 'ok'){
31432         // process text value...
31433     }
31434 });
31435
31436 // Show a dialog using config options:
31437 Roo.Msg.show({
31438    title:'Save Changes?',
31439    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31440    buttons: Roo.Msg.YESNOCANCEL,
31441    fn: processResult,
31442    animEl: 'elId'
31443 });
31444 </code></pre>
31445  * @singleton
31446  */
31447 Roo.MessageBox = function(){
31448     var dlg, opt, mask, waitTimer;
31449     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31450     var buttons, activeTextEl, bwidth;
31451
31452     // private
31453     var handleButton = function(button){
31454         dlg.hide();
31455         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31456     };
31457
31458     // private
31459     var handleHide = function(){
31460         if(opt && opt.cls){
31461             dlg.el.removeClass(opt.cls);
31462         }
31463         if(waitTimer){
31464             Roo.TaskMgr.stop(waitTimer);
31465             waitTimer = null;
31466         }
31467     };
31468
31469     // private
31470     var updateButtons = function(b){
31471         var width = 0;
31472         if(!b){
31473             buttons["ok"].hide();
31474             buttons["cancel"].hide();
31475             buttons["yes"].hide();
31476             buttons["no"].hide();
31477             dlg.footer.dom.style.display = 'none';
31478             return width;
31479         }
31480         dlg.footer.dom.style.display = '';
31481         for(var k in buttons){
31482             if(typeof buttons[k] != "function"){
31483                 if(b[k]){
31484                     buttons[k].show();
31485                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31486                     width += buttons[k].el.getWidth()+15;
31487                 }else{
31488                     buttons[k].hide();
31489                 }
31490             }
31491         }
31492         return width;
31493     };
31494
31495     // private
31496     var handleEsc = function(d, k, e){
31497         if(opt && opt.closable !== false){
31498             dlg.hide();
31499         }
31500         if(e){
31501             e.stopEvent();
31502         }
31503     };
31504
31505     return {
31506         /**
31507          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31508          * @return {Roo.BasicDialog} The BasicDialog element
31509          */
31510         getDialog : function(){
31511            if(!dlg){
31512                 dlg = new Roo.BasicDialog("x-msg-box", {
31513                     autoCreate : true,
31514                     shadow: true,
31515                     draggable: true,
31516                     resizable:false,
31517                     constraintoviewport:false,
31518                     fixedcenter:true,
31519                     collapsible : false,
31520                     shim:true,
31521                     modal: true,
31522                     width:400, height:100,
31523                     buttonAlign:"center",
31524                     closeClick : function(){
31525                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31526                             handleButton("no");
31527                         }else{
31528                             handleButton("cancel");
31529                         }
31530                     }
31531                 });
31532                 dlg.on("hide", handleHide);
31533                 mask = dlg.mask;
31534                 dlg.addKeyListener(27, handleEsc);
31535                 buttons = {};
31536                 var bt = this.buttonText;
31537                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31538                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31539                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31540                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31541                 bodyEl = dlg.body.createChild({
31542
31543                     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>'
31544                 });
31545                 msgEl = bodyEl.dom.firstChild;
31546                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31547                 textboxEl.enableDisplayMode();
31548                 textboxEl.addKeyListener([10,13], function(){
31549                     if(dlg.isVisible() && opt && opt.buttons){
31550                         if(opt.buttons.ok){
31551                             handleButton("ok");
31552                         }else if(opt.buttons.yes){
31553                             handleButton("yes");
31554                         }
31555                     }
31556                 });
31557                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31558                 textareaEl.enableDisplayMode();
31559                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31560                 progressEl.enableDisplayMode();
31561                 var pf = progressEl.dom.firstChild;
31562                 if (pf) {
31563                     pp = Roo.get(pf.firstChild);
31564                     pp.setHeight(pf.offsetHeight);
31565                 }
31566                 
31567             }
31568             return dlg;
31569         },
31570
31571         /**
31572          * Updates the message box body text
31573          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31574          * the XHTML-compliant non-breaking space character '&amp;#160;')
31575          * @return {Roo.MessageBox} This message box
31576          */
31577         updateText : function(text){
31578             if(!dlg.isVisible() && !opt.width){
31579                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31580             }
31581             msgEl.innerHTML = text || '&#160;';
31582       
31583             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31584             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31585             var w = Math.max(
31586                     Math.min(opt.width || cw , this.maxWidth), 
31587                     Math.max(opt.minWidth || this.minWidth, bwidth)
31588             );
31589             if(opt.prompt){
31590                 activeTextEl.setWidth(w);
31591             }
31592             if(dlg.isVisible()){
31593                 dlg.fixedcenter = false;
31594             }
31595             // to big, make it scroll. = But as usual stupid IE does not support
31596             // !important..
31597             
31598             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31599                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31600                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31601             } else {
31602                 bodyEl.dom.style.height = '';
31603                 bodyEl.dom.style.overflowY = '';
31604             }
31605             if (cw > w) {
31606                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31607             } else {
31608                 bodyEl.dom.style.overflowX = '';
31609             }
31610             
31611             dlg.setContentSize(w, bodyEl.getHeight());
31612             if(dlg.isVisible()){
31613                 dlg.fixedcenter = true;
31614             }
31615             return this;
31616         },
31617
31618         /**
31619          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31620          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31621          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31622          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31623          * @return {Roo.MessageBox} This message box
31624          */
31625         updateProgress : function(value, text){
31626             if(text){
31627                 this.updateText(text);
31628             }
31629             if (pp) { // weird bug on my firefox - for some reason this is not defined
31630                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31631             }
31632             return this;
31633         },        
31634
31635         /**
31636          * Returns true if the message box is currently displayed
31637          * @return {Boolean} True if the message box is visible, else false
31638          */
31639         isVisible : function(){
31640             return dlg && dlg.isVisible();  
31641         },
31642
31643         /**
31644          * Hides the message box if it is displayed
31645          */
31646         hide : function(){
31647             if(this.isVisible()){
31648                 dlg.hide();
31649             }  
31650         },
31651
31652         /**
31653          * Displays a new message box, or reinitializes an existing message box, based on the config options
31654          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31655          * The following config object properties are supported:
31656          * <pre>
31657 Property    Type             Description
31658 ----------  ---------------  ------------------------------------------------------------------------------------
31659 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31660                                    closes (defaults to undefined)
31661 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31662                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31663 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31664                                    progress and wait dialogs will ignore this property and always hide the
31665                                    close button as they can only be closed programmatically.
31666 cls               String           A custom CSS class to apply to the message box element
31667 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31668                                    displayed (defaults to 75)
31669 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31670                                    function will be btn (the name of the button that was clicked, if applicable,
31671                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31672                                    Progress and wait dialogs will ignore this option since they do not respond to
31673                                    user actions and can only be closed programmatically, so any required function
31674                                    should be called by the same code after it closes the dialog.
31675 icon              String           A CSS class that provides a background image to be used as an icon for
31676                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31677 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31678 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31679 modal             Boolean          False to allow user interaction with the page while the message box is
31680                                    displayed (defaults to true)
31681 msg               String           A string that will replace the existing message box body text (defaults
31682                                    to the XHTML-compliant non-breaking space character '&#160;')
31683 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31684 progress          Boolean          True to display a progress bar (defaults to false)
31685 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31686 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31687 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31688 title             String           The title text
31689 value             String           The string value to set into the active textbox element if displayed
31690 wait              Boolean          True to display a progress bar (defaults to false)
31691 width             Number           The width of the dialog in pixels
31692 </pre>
31693          *
31694          * Example usage:
31695          * <pre><code>
31696 Roo.Msg.show({
31697    title: 'Address',
31698    msg: 'Please enter your address:',
31699    width: 300,
31700    buttons: Roo.MessageBox.OKCANCEL,
31701    multiline: true,
31702    fn: saveAddress,
31703    animEl: 'addAddressBtn'
31704 });
31705 </code></pre>
31706          * @param {Object} config Configuration options
31707          * @return {Roo.MessageBox} This message box
31708          */
31709         show : function(options)
31710         {
31711             
31712             // this causes nightmares if you show one dialog after another
31713             // especially on callbacks..
31714              
31715             if(this.isVisible()){
31716                 
31717                 this.hide();
31718                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31719                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31720                 Roo.log("New Dialog Message:" +  options.msg )
31721                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31722                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31723                 
31724             }
31725             var d = this.getDialog();
31726             opt = options;
31727             d.setTitle(opt.title || "&#160;");
31728             d.close.setDisplayed(opt.closable !== false);
31729             activeTextEl = textboxEl;
31730             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31731             if(opt.prompt){
31732                 if(opt.multiline){
31733                     textboxEl.hide();
31734                     textareaEl.show();
31735                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31736                         opt.multiline : this.defaultTextHeight);
31737                     activeTextEl = textareaEl;
31738                 }else{
31739                     textboxEl.show();
31740                     textareaEl.hide();
31741                 }
31742             }else{
31743                 textboxEl.hide();
31744                 textareaEl.hide();
31745             }
31746             progressEl.setDisplayed(opt.progress === true);
31747             this.updateProgress(0);
31748             activeTextEl.dom.value = opt.value || "";
31749             if(opt.prompt){
31750                 dlg.setDefaultButton(activeTextEl);
31751             }else{
31752                 var bs = opt.buttons;
31753                 var db = null;
31754                 if(bs && bs.ok){
31755                     db = buttons["ok"];
31756                 }else if(bs && bs.yes){
31757                     db = buttons["yes"];
31758                 }
31759                 dlg.setDefaultButton(db);
31760             }
31761             bwidth = updateButtons(opt.buttons);
31762             this.updateText(opt.msg);
31763             if(opt.cls){
31764                 d.el.addClass(opt.cls);
31765             }
31766             d.proxyDrag = opt.proxyDrag === true;
31767             d.modal = opt.modal !== false;
31768             d.mask = opt.modal !== false ? mask : false;
31769             if(!d.isVisible()){
31770                 // force it to the end of the z-index stack so it gets a cursor in FF
31771                 document.body.appendChild(dlg.el.dom);
31772                 d.animateTarget = null;
31773                 d.show(options.animEl);
31774             }
31775             return this;
31776         },
31777
31778         /**
31779          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31780          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31781          * and closing the message box when the process is complete.
31782          * @param {String} title The title bar text
31783          * @param {String} msg The message box body text
31784          * @return {Roo.MessageBox} This message box
31785          */
31786         progress : function(title, msg){
31787             this.show({
31788                 title : title,
31789                 msg : msg,
31790                 buttons: false,
31791                 progress:true,
31792                 closable:false,
31793                 minWidth: this.minProgressWidth,
31794                 modal : true
31795             });
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31801          * If a callback function is passed it will be called after the user clicks the button, and the
31802          * id of the button that was clicked will be passed as the only parameter to the callback
31803          * (could also be the top-right close button).
31804          * @param {String} title The title bar text
31805          * @param {String} msg The message box body text
31806          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31807          * @param {Object} scope (optional) The scope of the callback function
31808          * @return {Roo.MessageBox} This message box
31809          */
31810         alert : function(title, msg, fn, scope){
31811             this.show({
31812                 title : title,
31813                 msg : msg,
31814                 buttons: this.OK,
31815                 fn: fn,
31816                 scope : scope,
31817                 modal : true
31818             });
31819             return this;
31820         },
31821
31822         /**
31823          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31824          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31825          * You are responsible for closing the message box when the process is complete.
31826          * @param {String} msg The message box body text
31827          * @param {String} title (optional) The title bar text
31828          * @return {Roo.MessageBox} This message box
31829          */
31830         wait : function(msg, title){
31831             this.show({
31832                 title : title,
31833                 msg : msg,
31834                 buttons: false,
31835                 closable:false,
31836                 progress:true,
31837                 modal:true,
31838                 width:300,
31839                 wait:true
31840             });
31841             waitTimer = Roo.TaskMgr.start({
31842                 run: function(i){
31843                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31844                 },
31845                 interval: 1000
31846             });
31847             return this;
31848         },
31849
31850         /**
31851          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31852          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31853          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31854          * @param {String} title The title bar text
31855          * @param {String} msg The message box body text
31856          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31857          * @param {Object} scope (optional) The scope of the callback function
31858          * @return {Roo.MessageBox} This message box
31859          */
31860         confirm : function(title, msg, fn, scope){
31861             this.show({
31862                 title : title,
31863                 msg : msg,
31864                 buttons: this.YESNO,
31865                 fn: fn,
31866                 scope : scope,
31867                 modal : true
31868             });
31869             return this;
31870         },
31871
31872         /**
31873          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31874          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31875          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31876          * (could also be the top-right close button) and the text that was entered will be passed as the two
31877          * parameters to the callback.
31878          * @param {String} title The title bar text
31879          * @param {String} msg The message box body text
31880          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31881          * @param {Object} scope (optional) The scope of the callback function
31882          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31883          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31884          * @return {Roo.MessageBox} This message box
31885          */
31886         prompt : function(title, msg, fn, scope, multiline){
31887             this.show({
31888                 title : title,
31889                 msg : msg,
31890                 buttons: this.OKCANCEL,
31891                 fn: fn,
31892                 minWidth:250,
31893                 scope : scope,
31894                 prompt:true,
31895                 multiline: multiline,
31896                 modal : true
31897             });
31898             return this;
31899         },
31900
31901         /**
31902          * Button config that displays a single OK button
31903          * @type Object
31904          */
31905         OK : {ok:true},
31906         /**
31907          * Button config that displays Yes and No buttons
31908          * @type Object
31909          */
31910         YESNO : {yes:true, no:true},
31911         /**
31912          * Button config that displays OK and Cancel buttons
31913          * @type Object
31914          */
31915         OKCANCEL : {ok:true, cancel:true},
31916         /**
31917          * Button config that displays Yes, No and Cancel buttons
31918          * @type Object
31919          */
31920         YESNOCANCEL : {yes:true, no:true, cancel:true},
31921
31922         /**
31923          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31924          * @type Number
31925          */
31926         defaultTextHeight : 75,
31927         /**
31928          * The maximum width in pixels of the message box (defaults to 600)
31929          * @type Number
31930          */
31931         maxWidth : 600,
31932         /**
31933          * The minimum width in pixels of the message box (defaults to 100)
31934          * @type Number
31935          */
31936         minWidth : 100,
31937         /**
31938          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31939          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31940          * @type Number
31941          */
31942         minProgressWidth : 250,
31943         /**
31944          * An object containing the default button text strings that can be overriden for localized language support.
31945          * Supported properties are: ok, cancel, yes and no.
31946          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31947          * @type Object
31948          */
31949         buttonText : {
31950             ok : "OK",
31951             cancel : "Cancel",
31952             yes : "Yes",
31953             no : "No"
31954         }
31955     };
31956 }();
31957
31958 /**
31959  * Shorthand for {@link Roo.MessageBox}
31960  */
31961 Roo.Msg = Roo.MessageBox;/*
31962  * Based on:
31963  * Ext JS Library 1.1.1
31964  * Copyright(c) 2006-2007, Ext JS, LLC.
31965  *
31966  * Originally Released Under LGPL - original licence link has changed is not relivant.
31967  *
31968  * Fork - LGPL
31969  * <script type="text/javascript">
31970  */
31971 /**
31972  * @class Roo.QuickTips
31973  * Provides attractive and customizable tooltips for any element.
31974  * @singleton
31975  */
31976 Roo.QuickTips = function(){
31977     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31978     var ce, bd, xy, dd;
31979     var visible = false, disabled = true, inited = false;
31980     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31981     
31982     var onOver = function(e){
31983         if(disabled){
31984             return;
31985         }
31986         var t = e.getTarget();
31987         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31988             return;
31989         }
31990         if(ce && t == ce.el){
31991             clearTimeout(hideProc);
31992             return;
31993         }
31994         if(t && tagEls[t.id]){
31995             tagEls[t.id].el = t;
31996             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31997             return;
31998         }
31999         var ttp, et = Roo.fly(t);
32000         var ns = cfg.namespace;
32001         if(tm.interceptTitles && t.title){
32002             ttp = t.title;
32003             t.qtip = ttp;
32004             t.removeAttribute("title");
32005             e.preventDefault();
32006         }else{
32007             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32008         }
32009         if(ttp){
32010             showProc = show.defer(tm.showDelay, tm, [{
32011                 el: t, 
32012                 text: ttp, 
32013                 width: et.getAttributeNS(ns, cfg.width),
32014                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32015                 title: et.getAttributeNS(ns, cfg.title),
32016                     cls: et.getAttributeNS(ns, cfg.cls)
32017             }]);
32018         }
32019     };
32020     
32021     var onOut = function(e){
32022         clearTimeout(showProc);
32023         var t = e.getTarget();
32024         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32025             hideProc = setTimeout(hide, tm.hideDelay);
32026         }
32027     };
32028     
32029     var onMove = function(e){
32030         if(disabled){
32031             return;
32032         }
32033         xy = e.getXY();
32034         xy[1] += 18;
32035         if(tm.trackMouse && ce){
32036             el.setXY(xy);
32037         }
32038     };
32039     
32040     var onDown = function(e){
32041         clearTimeout(showProc);
32042         clearTimeout(hideProc);
32043         if(!e.within(el)){
32044             if(tm.hideOnClick){
32045                 hide();
32046                 tm.disable();
32047                 tm.enable.defer(100, tm);
32048             }
32049         }
32050     };
32051     
32052     var getPad = function(){
32053         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32054     };
32055
32056     var show = function(o){
32057         if(disabled){
32058             return;
32059         }
32060         clearTimeout(dismissProc);
32061         ce = o;
32062         if(removeCls){ // in case manually hidden
32063             el.removeClass(removeCls);
32064             removeCls = null;
32065         }
32066         if(ce.cls){
32067             el.addClass(ce.cls);
32068             removeCls = ce.cls;
32069         }
32070         if(ce.title){
32071             tipTitle.update(ce.title);
32072             tipTitle.show();
32073         }else{
32074             tipTitle.update('');
32075             tipTitle.hide();
32076         }
32077         el.dom.style.width  = tm.maxWidth+'px';
32078         //tipBody.dom.style.width = '';
32079         tipBodyText.update(o.text);
32080         var p = getPad(), w = ce.width;
32081         if(!w){
32082             var td = tipBodyText.dom;
32083             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32084             if(aw > tm.maxWidth){
32085                 w = tm.maxWidth;
32086             }else if(aw < tm.minWidth){
32087                 w = tm.minWidth;
32088             }else{
32089                 w = aw;
32090             }
32091         }
32092         //tipBody.setWidth(w);
32093         el.setWidth(parseInt(w, 10) + p);
32094         if(ce.autoHide === false){
32095             close.setDisplayed(true);
32096             if(dd){
32097                 dd.unlock();
32098             }
32099         }else{
32100             close.setDisplayed(false);
32101             if(dd){
32102                 dd.lock();
32103             }
32104         }
32105         if(xy){
32106             el.avoidY = xy[1]-18;
32107             el.setXY(xy);
32108         }
32109         if(tm.animate){
32110             el.setOpacity(.1);
32111             el.setStyle("visibility", "visible");
32112             el.fadeIn({callback: afterShow});
32113         }else{
32114             afterShow();
32115         }
32116     };
32117     
32118     var afterShow = function(){
32119         if(ce){
32120             el.show();
32121             esc.enable();
32122             if(tm.autoDismiss && ce.autoHide !== false){
32123                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32124             }
32125         }
32126     };
32127     
32128     var hide = function(noanim){
32129         clearTimeout(dismissProc);
32130         clearTimeout(hideProc);
32131         ce = null;
32132         if(el.isVisible()){
32133             esc.disable();
32134             if(noanim !== true && tm.animate){
32135                 el.fadeOut({callback: afterHide});
32136             }else{
32137                 afterHide();
32138             } 
32139         }
32140     };
32141     
32142     var afterHide = function(){
32143         el.hide();
32144         if(removeCls){
32145             el.removeClass(removeCls);
32146             removeCls = null;
32147         }
32148     };
32149     
32150     return {
32151         /**
32152         * @cfg {Number} minWidth
32153         * The minimum width of the quick tip (defaults to 40)
32154         */
32155        minWidth : 40,
32156         /**
32157         * @cfg {Number} maxWidth
32158         * The maximum width of the quick tip (defaults to 300)
32159         */
32160        maxWidth : 300,
32161         /**
32162         * @cfg {Boolean} interceptTitles
32163         * True to automatically use the element's DOM title value if available (defaults to false)
32164         */
32165        interceptTitles : false,
32166         /**
32167         * @cfg {Boolean} trackMouse
32168         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32169         */
32170        trackMouse : false,
32171         /**
32172         * @cfg {Boolean} hideOnClick
32173         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32174         */
32175        hideOnClick : true,
32176         /**
32177         * @cfg {Number} showDelay
32178         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32179         */
32180        showDelay : 500,
32181         /**
32182         * @cfg {Number} hideDelay
32183         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32184         */
32185        hideDelay : 200,
32186         /**
32187         * @cfg {Boolean} autoHide
32188         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32189         * Used in conjunction with hideDelay.
32190         */
32191        autoHide : true,
32192         /**
32193         * @cfg {Boolean}
32194         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32195         * (defaults to true).  Used in conjunction with autoDismissDelay.
32196         */
32197        autoDismiss : true,
32198         /**
32199         * @cfg {Number}
32200         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32201         */
32202        autoDismissDelay : 5000,
32203        /**
32204         * @cfg {Boolean} animate
32205         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32206         */
32207        animate : false,
32208
32209        /**
32210         * @cfg {String} title
32211         * Title text to display (defaults to '').  This can be any valid HTML markup.
32212         */
32213         title: '',
32214        /**
32215         * @cfg {String} text
32216         * Body text to display (defaults to '').  This can be any valid HTML markup.
32217         */
32218         text : '',
32219        /**
32220         * @cfg {String} cls
32221         * A CSS class to apply to the base quick tip element (defaults to '').
32222         */
32223         cls : '',
32224        /**
32225         * @cfg {Number} width
32226         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32227         * minWidth or maxWidth.
32228         */
32229         width : null,
32230
32231     /**
32232      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32233      * or display QuickTips in a page.
32234      */
32235        init : function(){
32236           tm = Roo.QuickTips;
32237           cfg = tm.tagConfig;
32238           if(!inited){
32239               if(!Roo.isReady){ // allow calling of init() before onReady
32240                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32241                   return;
32242               }
32243               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32244               el.fxDefaults = {stopFx: true};
32245               // maximum custom styling
32246               //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>');
32247               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>');              
32248               tipTitle = el.child('h3');
32249               tipTitle.enableDisplayMode("block");
32250               tipBody = el.child('div.x-tip-bd');
32251               tipBodyText = el.child('div.x-tip-bd-inner');
32252               //bdLeft = el.child('div.x-tip-bd-left');
32253               //bdRight = el.child('div.x-tip-bd-right');
32254               close = el.child('div.x-tip-close');
32255               close.enableDisplayMode("block");
32256               close.on("click", hide);
32257               var d = Roo.get(document);
32258               d.on("mousedown", onDown);
32259               d.on("mouseover", onOver);
32260               d.on("mouseout", onOut);
32261               d.on("mousemove", onMove);
32262               esc = d.addKeyListener(27, hide);
32263               esc.disable();
32264               if(Roo.dd.DD){
32265                   dd = el.initDD("default", null, {
32266                       onDrag : function(){
32267                           el.sync();  
32268                       }
32269                   });
32270                   dd.setHandleElId(tipTitle.id);
32271                   dd.lock();
32272               }
32273               inited = true;
32274           }
32275           this.enable(); 
32276        },
32277
32278     /**
32279      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32280      * are supported:
32281      * <pre>
32282 Property    Type                   Description
32283 ----------  ---------------------  ------------------------------------------------------------------------
32284 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32285      * </ul>
32286      * @param {Object} config The config object
32287      */
32288        register : function(config){
32289            var cs = config instanceof Array ? config : arguments;
32290            for(var i = 0, len = cs.length; i < len; i++) {
32291                var c = cs[i];
32292                var target = c.target;
32293                if(target){
32294                    if(target instanceof Array){
32295                        for(var j = 0, jlen = target.length; j < jlen; j++){
32296                            tagEls[target[j]] = c;
32297                        }
32298                    }else{
32299                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32300                    }
32301                }
32302            }
32303        },
32304
32305     /**
32306      * Removes this quick tip from its element and destroys it.
32307      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32308      */
32309        unregister : function(el){
32310            delete tagEls[Roo.id(el)];
32311        },
32312
32313     /**
32314      * Enable this quick tip.
32315      */
32316        enable : function(){
32317            if(inited && disabled){
32318                locks.pop();
32319                if(locks.length < 1){
32320                    disabled = false;
32321                }
32322            }
32323        },
32324
32325     /**
32326      * Disable this quick tip.
32327      */
32328        disable : function(){
32329           disabled = true;
32330           clearTimeout(showProc);
32331           clearTimeout(hideProc);
32332           clearTimeout(dismissProc);
32333           if(ce){
32334               hide(true);
32335           }
32336           locks.push(1);
32337        },
32338
32339     /**
32340      * Returns true if the quick tip is enabled, else false.
32341      */
32342        isEnabled : function(){
32343             return !disabled;
32344        },
32345
32346         // private
32347        tagConfig : {
32348            namespace : "ext",
32349            attribute : "qtip",
32350            width : "width",
32351            target : "target",
32352            title : "qtitle",
32353            hide : "hide",
32354            cls : "qclass"
32355        }
32356    };
32357 }();
32358
32359 // backwards compat
32360 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32361  * Based on:
32362  * Ext JS Library 1.1.1
32363  * Copyright(c) 2006-2007, Ext JS, LLC.
32364  *
32365  * Originally Released Under LGPL - original licence link has changed is not relivant.
32366  *
32367  * Fork - LGPL
32368  * <script type="text/javascript">
32369  */
32370  
32371
32372 /**
32373  * @class Roo.tree.TreePanel
32374  * @extends Roo.data.Tree
32375
32376  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32377  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32378  * @cfg {Boolean} enableDD true to enable drag and drop
32379  * @cfg {Boolean} enableDrag true to enable just drag
32380  * @cfg {Boolean} enableDrop true to enable just drop
32381  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32382  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32383  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32384  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32385  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32386  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32387  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32388  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32389  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32390  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32391  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32392  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32393  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32394  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32395  * @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>
32396  * @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>
32397  * 
32398  * @constructor
32399  * @param {String/HTMLElement/Element} el The container element
32400  * @param {Object} config
32401  */
32402 Roo.tree.TreePanel = function(el, config){
32403     var root = false;
32404     var loader = false;
32405     if (config.root) {
32406         root = config.root;
32407         delete config.root;
32408     }
32409     if (config.loader) {
32410         loader = config.loader;
32411         delete config.loader;
32412     }
32413     
32414     Roo.apply(this, config);
32415     Roo.tree.TreePanel.superclass.constructor.call(this);
32416     this.el = Roo.get(el);
32417     this.el.addClass('x-tree');
32418     //console.log(root);
32419     if (root) {
32420         this.setRootNode( Roo.factory(root, Roo.tree));
32421     }
32422     if (loader) {
32423         this.loader = Roo.factory(loader, Roo.tree);
32424     }
32425    /**
32426     * Read-only. The id of the container element becomes this TreePanel's id.
32427     */
32428     this.id = this.el.id;
32429     this.addEvents({
32430         /**
32431         * @event beforeload
32432         * Fires before a node is loaded, return false to cancel
32433         * @param {Node} node The node being loaded
32434         */
32435         "beforeload" : true,
32436         /**
32437         * @event load
32438         * Fires when a node is loaded
32439         * @param {Node} node The node that was loaded
32440         */
32441         "load" : true,
32442         /**
32443         * @event textchange
32444         * Fires when the text for a node is changed
32445         * @param {Node} node The node
32446         * @param {String} text The new text
32447         * @param {String} oldText The old text
32448         */
32449         "textchange" : true,
32450         /**
32451         * @event beforeexpand
32452         * Fires before a node is expanded, return false to cancel.
32453         * @param {Node} node The node
32454         * @param {Boolean} deep
32455         * @param {Boolean} anim
32456         */
32457         "beforeexpand" : true,
32458         /**
32459         * @event beforecollapse
32460         * Fires before a node is collapsed, return false to cancel.
32461         * @param {Node} node The node
32462         * @param {Boolean} deep
32463         * @param {Boolean} anim
32464         */
32465         "beforecollapse" : true,
32466         /**
32467         * @event expand
32468         * Fires when a node is expanded
32469         * @param {Node} node The node
32470         */
32471         "expand" : true,
32472         /**
32473         * @event disabledchange
32474         * Fires when the disabled status of a node changes
32475         * @param {Node} node The node
32476         * @param {Boolean} disabled
32477         */
32478         "disabledchange" : true,
32479         /**
32480         * @event collapse
32481         * Fires when a node is collapsed
32482         * @param {Node} node The node
32483         */
32484         "collapse" : true,
32485         /**
32486         * @event beforeclick
32487         * Fires before click processing on a node. Return false to cancel the default action.
32488         * @param {Node} node The node
32489         * @param {Roo.EventObject} e The event object
32490         */
32491         "beforeclick":true,
32492         /**
32493         * @event checkchange
32494         * Fires when a node with a checkbox's checked property changes
32495         * @param {Node} this This node
32496         * @param {Boolean} checked
32497         */
32498         "checkchange":true,
32499         /**
32500         * @event click
32501         * Fires when a node is clicked
32502         * @param {Node} node The node
32503         * @param {Roo.EventObject} e The event object
32504         */
32505         "click":true,
32506         /**
32507         * @event dblclick
32508         * Fires when a node is double clicked
32509         * @param {Node} node The node
32510         * @param {Roo.EventObject} e The event object
32511         */
32512         "dblclick":true,
32513         /**
32514         * @event contextmenu
32515         * Fires when a node is right clicked
32516         * @param {Node} node The node
32517         * @param {Roo.EventObject} e The event object
32518         */
32519         "contextmenu":true,
32520         /**
32521         * @event beforechildrenrendered
32522         * Fires right before the child nodes for a node are rendered
32523         * @param {Node} node The node
32524         */
32525         "beforechildrenrendered":true,
32526         /**
32527         * @event startdrag
32528         * Fires when a node starts being dragged
32529         * @param {Roo.tree.TreePanel} this
32530         * @param {Roo.tree.TreeNode} node
32531         * @param {event} e The raw browser event
32532         */ 
32533        "startdrag" : true,
32534        /**
32535         * @event enddrag
32536         * Fires when a drag operation is complete
32537         * @param {Roo.tree.TreePanel} this
32538         * @param {Roo.tree.TreeNode} node
32539         * @param {event} e The raw browser event
32540         */
32541        "enddrag" : true,
32542        /**
32543         * @event dragdrop
32544         * Fires when a dragged node is dropped on a valid DD target
32545         * @param {Roo.tree.TreePanel} this
32546         * @param {Roo.tree.TreeNode} node
32547         * @param {DD} dd The dd it was dropped on
32548         * @param {event} e The raw browser event
32549         */
32550        "dragdrop" : true,
32551        /**
32552         * @event beforenodedrop
32553         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32554         * passed to handlers has the following properties:<br />
32555         * <ul style="padding:5px;padding-left:16px;">
32556         * <li>tree - The TreePanel</li>
32557         * <li>target - The node being targeted for the drop</li>
32558         * <li>data - The drag data from the drag source</li>
32559         * <li>point - The point of the drop - append, above or below</li>
32560         * <li>source - The drag source</li>
32561         * <li>rawEvent - Raw mouse event</li>
32562         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32563         * to be inserted by setting them on this object.</li>
32564         * <li>cancel - Set this to true to cancel the drop.</li>
32565         * </ul>
32566         * @param {Object} dropEvent
32567         */
32568        "beforenodedrop" : true,
32569        /**
32570         * @event nodedrop
32571         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32572         * passed to handlers has the following properties:<br />
32573         * <ul style="padding:5px;padding-left:16px;">
32574         * <li>tree - The TreePanel</li>
32575         * <li>target - The node being targeted for the drop</li>
32576         * <li>data - The drag data from the drag source</li>
32577         * <li>point - The point of the drop - append, above or below</li>
32578         * <li>source - The drag source</li>
32579         * <li>rawEvent - Raw mouse event</li>
32580         * <li>dropNode - Dropped node(s).</li>
32581         * </ul>
32582         * @param {Object} dropEvent
32583         */
32584        "nodedrop" : true,
32585         /**
32586         * @event nodedragover
32587         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32588         * passed to handlers has the following properties:<br />
32589         * <ul style="padding:5px;padding-left:16px;">
32590         * <li>tree - The TreePanel</li>
32591         * <li>target - The node being targeted for the drop</li>
32592         * <li>data - The drag data from the drag source</li>
32593         * <li>point - The point of the drop - append, above or below</li>
32594         * <li>source - The drag source</li>
32595         * <li>rawEvent - Raw mouse event</li>
32596         * <li>dropNode - Drop node(s) provided by the source.</li>
32597         * <li>cancel - Set this to true to signal drop not allowed.</li>
32598         * </ul>
32599         * @param {Object} dragOverEvent
32600         */
32601        "nodedragover" : true
32602         
32603     });
32604     if(this.singleExpand){
32605        this.on("beforeexpand", this.restrictExpand, this);
32606     }
32607     if (this.editor) {
32608         this.editor.tree = this;
32609         this.editor = Roo.factory(this.editor, Roo.tree);
32610     }
32611     
32612     if (this.selModel) {
32613         this.selModel = Roo.factory(this.selModel, Roo.tree);
32614     }
32615    
32616 };
32617 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32618     rootVisible : true,
32619     animate: Roo.enableFx,
32620     lines : true,
32621     enableDD : false,
32622     hlDrop : Roo.enableFx,
32623   
32624     renderer: false,
32625     
32626     rendererTip: false,
32627     // private
32628     restrictExpand : function(node){
32629         var p = node.parentNode;
32630         if(p){
32631             if(p.expandedChild && p.expandedChild.parentNode == p){
32632                 p.expandedChild.collapse();
32633             }
32634             p.expandedChild = node;
32635         }
32636     },
32637
32638     // private override
32639     setRootNode : function(node){
32640         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32641         if(!this.rootVisible){
32642             node.ui = new Roo.tree.RootTreeNodeUI(node);
32643         }
32644         return node;
32645     },
32646
32647     /**
32648      * Returns the container element for this TreePanel
32649      */
32650     getEl : function(){
32651         return this.el;
32652     },
32653
32654     /**
32655      * Returns the default TreeLoader for this TreePanel
32656      */
32657     getLoader : function(){
32658         return this.loader;
32659     },
32660
32661     /**
32662      * Expand all nodes
32663      */
32664     expandAll : function(){
32665         this.root.expand(true);
32666     },
32667
32668     /**
32669      * Collapse all nodes
32670      */
32671     collapseAll : function(){
32672         this.root.collapse(true);
32673     },
32674
32675     /**
32676      * Returns the selection model used by this TreePanel
32677      */
32678     getSelectionModel : function(){
32679         if(!this.selModel){
32680             this.selModel = new Roo.tree.DefaultSelectionModel();
32681         }
32682         return this.selModel;
32683     },
32684
32685     /**
32686      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32687      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32688      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32689      * @return {Array}
32690      */
32691     getChecked : function(a, startNode){
32692         startNode = startNode || this.root;
32693         var r = [];
32694         var f = function(){
32695             if(this.attributes.checked){
32696                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32697             }
32698         }
32699         startNode.cascade(f);
32700         return r;
32701     },
32702
32703     /**
32704      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32705      * @param {String} path
32706      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32707      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32708      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32709      */
32710     expandPath : function(path, attr, callback){
32711         attr = attr || "id";
32712         var keys = path.split(this.pathSeparator);
32713         var curNode = this.root;
32714         if(curNode.attributes[attr] != keys[1]){ // invalid root
32715             if(callback){
32716                 callback(false, null);
32717             }
32718             return;
32719         }
32720         var index = 1;
32721         var f = function(){
32722             if(++index == keys.length){
32723                 if(callback){
32724                     callback(true, curNode);
32725                 }
32726                 return;
32727             }
32728             var c = curNode.findChild(attr, keys[index]);
32729             if(!c){
32730                 if(callback){
32731                     callback(false, curNode);
32732                 }
32733                 return;
32734             }
32735             curNode = c;
32736             c.expand(false, false, f);
32737         };
32738         curNode.expand(false, false, f);
32739     },
32740
32741     /**
32742      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32743      * @param {String} path
32744      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32745      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32746      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32747      */
32748     selectPath : function(path, attr, callback){
32749         attr = attr || "id";
32750         var keys = path.split(this.pathSeparator);
32751         var v = keys.pop();
32752         if(keys.length > 0){
32753             var f = function(success, node){
32754                 if(success && node){
32755                     var n = node.findChild(attr, v);
32756                     if(n){
32757                         n.select();
32758                         if(callback){
32759                             callback(true, n);
32760                         }
32761                     }else if(callback){
32762                         callback(false, n);
32763                     }
32764                 }else{
32765                     if(callback){
32766                         callback(false, n);
32767                     }
32768                 }
32769             };
32770             this.expandPath(keys.join(this.pathSeparator), attr, f);
32771         }else{
32772             this.root.select();
32773             if(callback){
32774                 callback(true, this.root);
32775             }
32776         }
32777     },
32778
32779     getTreeEl : function(){
32780         return this.el;
32781     },
32782
32783     /**
32784      * Trigger rendering of this TreePanel
32785      */
32786     render : function(){
32787         if (this.innerCt) {
32788             return this; // stop it rendering more than once!!
32789         }
32790         
32791         this.innerCt = this.el.createChild({tag:"ul",
32792                cls:"x-tree-root-ct " +
32793                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32794
32795         if(this.containerScroll){
32796             Roo.dd.ScrollManager.register(this.el);
32797         }
32798         if((this.enableDD || this.enableDrop) && !this.dropZone){
32799            /**
32800             * The dropZone used by this tree if drop is enabled
32801             * @type Roo.tree.TreeDropZone
32802             */
32803              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32804                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32805            });
32806         }
32807         if((this.enableDD || this.enableDrag) && !this.dragZone){
32808            /**
32809             * The dragZone used by this tree if drag is enabled
32810             * @type Roo.tree.TreeDragZone
32811             */
32812             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32813                ddGroup: this.ddGroup || "TreeDD",
32814                scroll: this.ddScroll
32815            });
32816         }
32817         this.getSelectionModel().init(this);
32818         if (!this.root) {
32819             Roo.log("ROOT not set in tree");
32820             return this;
32821         }
32822         this.root.render();
32823         if(!this.rootVisible){
32824             this.root.renderChildren();
32825         }
32826         return this;
32827     }
32828 });/*
32829  * Based on:
32830  * Ext JS Library 1.1.1
32831  * Copyright(c) 2006-2007, Ext JS, LLC.
32832  *
32833  * Originally Released Under LGPL - original licence link has changed is not relivant.
32834  *
32835  * Fork - LGPL
32836  * <script type="text/javascript">
32837  */
32838  
32839
32840 /**
32841  * @class Roo.tree.DefaultSelectionModel
32842  * @extends Roo.util.Observable
32843  * The default single selection for a TreePanel.
32844  * @param {Object} cfg Configuration
32845  */
32846 Roo.tree.DefaultSelectionModel = function(cfg){
32847    this.selNode = null;
32848    
32849    
32850    
32851    this.addEvents({
32852        /**
32853         * @event selectionchange
32854         * Fires when the selected node changes
32855         * @param {DefaultSelectionModel} this
32856         * @param {TreeNode} node the new selection
32857         */
32858        "selectionchange" : true,
32859
32860        /**
32861         * @event beforeselect
32862         * Fires before the selected node changes, return false to cancel the change
32863         * @param {DefaultSelectionModel} this
32864         * @param {TreeNode} node the new selection
32865         * @param {TreeNode} node the old selection
32866         */
32867        "beforeselect" : true
32868    });
32869    
32870     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32871 };
32872
32873 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32874     init : function(tree){
32875         this.tree = tree;
32876         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32877         tree.on("click", this.onNodeClick, this);
32878     },
32879     
32880     onNodeClick : function(node, e){
32881         if (e.ctrlKey && this.selNode == node)  {
32882             this.unselect(node);
32883             return;
32884         }
32885         this.select(node);
32886     },
32887     
32888     /**
32889      * Select a node.
32890      * @param {TreeNode} node The node to select
32891      * @return {TreeNode} The selected node
32892      */
32893     select : function(node){
32894         var last = this.selNode;
32895         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32896             if(last){
32897                 last.ui.onSelectedChange(false);
32898             }
32899             this.selNode = node;
32900             node.ui.onSelectedChange(true);
32901             this.fireEvent("selectionchange", this, node, last);
32902         }
32903         return node;
32904     },
32905     
32906     /**
32907      * Deselect a node.
32908      * @param {TreeNode} node The node to unselect
32909      */
32910     unselect : function(node){
32911         if(this.selNode == node){
32912             this.clearSelections();
32913         }    
32914     },
32915     
32916     /**
32917      * Clear all selections
32918      */
32919     clearSelections : function(){
32920         var n = this.selNode;
32921         if(n){
32922             n.ui.onSelectedChange(false);
32923             this.selNode = null;
32924             this.fireEvent("selectionchange", this, null);
32925         }
32926         return n;
32927     },
32928     
32929     /**
32930      * Get the selected node
32931      * @return {TreeNode} The selected node
32932      */
32933     getSelectedNode : function(){
32934         return this.selNode;    
32935     },
32936     
32937     /**
32938      * Returns true if the node is selected
32939      * @param {TreeNode} node The node to check
32940      * @return {Boolean}
32941      */
32942     isSelected : function(node){
32943         return this.selNode == node;  
32944     },
32945
32946     /**
32947      * Selects the node above the selected node in the tree, intelligently walking the nodes
32948      * @return TreeNode The new selection
32949      */
32950     selectPrevious : function(){
32951         var s = this.selNode || this.lastSelNode;
32952         if(!s){
32953             return null;
32954         }
32955         var ps = s.previousSibling;
32956         if(ps){
32957             if(!ps.isExpanded() || ps.childNodes.length < 1){
32958                 return this.select(ps);
32959             } else{
32960                 var lc = ps.lastChild;
32961                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32962                     lc = lc.lastChild;
32963                 }
32964                 return this.select(lc);
32965             }
32966         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32967             return this.select(s.parentNode);
32968         }
32969         return null;
32970     },
32971
32972     /**
32973      * Selects the node above the selected node in the tree, intelligently walking the nodes
32974      * @return TreeNode The new selection
32975      */
32976     selectNext : function(){
32977         var s = this.selNode || this.lastSelNode;
32978         if(!s){
32979             return null;
32980         }
32981         if(s.firstChild && s.isExpanded()){
32982              return this.select(s.firstChild);
32983          }else if(s.nextSibling){
32984              return this.select(s.nextSibling);
32985          }else if(s.parentNode){
32986             var newS = null;
32987             s.parentNode.bubble(function(){
32988                 if(this.nextSibling){
32989                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32990                     return false;
32991                 }
32992             });
32993             return newS;
32994          }
32995         return null;
32996     },
32997
32998     onKeyDown : function(e){
32999         var s = this.selNode || this.lastSelNode;
33000         // undesirable, but required
33001         var sm = this;
33002         if(!s){
33003             return;
33004         }
33005         var k = e.getKey();
33006         switch(k){
33007              case e.DOWN:
33008                  e.stopEvent();
33009                  this.selectNext();
33010              break;
33011              case e.UP:
33012                  e.stopEvent();
33013                  this.selectPrevious();
33014              break;
33015              case e.RIGHT:
33016                  e.preventDefault();
33017                  if(s.hasChildNodes()){
33018                      if(!s.isExpanded()){
33019                          s.expand();
33020                      }else if(s.firstChild){
33021                          this.select(s.firstChild, e);
33022                      }
33023                  }
33024              break;
33025              case e.LEFT:
33026                  e.preventDefault();
33027                  if(s.hasChildNodes() && s.isExpanded()){
33028                      s.collapse();
33029                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33030                      this.select(s.parentNode, e);
33031                  }
33032              break;
33033         };
33034     }
33035 });
33036
33037 /**
33038  * @class Roo.tree.MultiSelectionModel
33039  * @extends Roo.util.Observable
33040  * Multi selection for a TreePanel.
33041  * @param {Object} cfg Configuration
33042  */
33043 Roo.tree.MultiSelectionModel = function(){
33044    this.selNodes = [];
33045    this.selMap = {};
33046    this.addEvents({
33047        /**
33048         * @event selectionchange
33049         * Fires when the selected nodes change
33050         * @param {MultiSelectionModel} this
33051         * @param {Array} nodes Array of the selected nodes
33052         */
33053        "selectionchange" : true
33054    });
33055    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33056    
33057 };
33058
33059 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33060     init : function(tree){
33061         this.tree = tree;
33062         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33063         tree.on("click", this.onNodeClick, this);
33064     },
33065     
33066     onNodeClick : function(node, e){
33067         this.select(node, e, e.ctrlKey);
33068     },
33069     
33070     /**
33071      * Select a node.
33072      * @param {TreeNode} node The node to select
33073      * @param {EventObject} e (optional) An event associated with the selection
33074      * @param {Boolean} keepExisting True to retain existing selections
33075      * @return {TreeNode} The selected node
33076      */
33077     select : function(node, e, keepExisting){
33078         if(keepExisting !== true){
33079             this.clearSelections(true);
33080         }
33081         if(this.isSelected(node)){
33082             this.lastSelNode = node;
33083             return node;
33084         }
33085         this.selNodes.push(node);
33086         this.selMap[node.id] = node;
33087         this.lastSelNode = node;
33088         node.ui.onSelectedChange(true);
33089         this.fireEvent("selectionchange", this, this.selNodes);
33090         return node;
33091     },
33092     
33093     /**
33094      * Deselect a node.
33095      * @param {TreeNode} node The node to unselect
33096      */
33097     unselect : function(node){
33098         if(this.selMap[node.id]){
33099             node.ui.onSelectedChange(false);
33100             var sn = this.selNodes;
33101             var index = -1;
33102             if(sn.indexOf){
33103                 index = sn.indexOf(node);
33104             }else{
33105                 for(var i = 0, len = sn.length; i < len; i++){
33106                     if(sn[i] == node){
33107                         index = i;
33108                         break;
33109                     }
33110                 }
33111             }
33112             if(index != -1){
33113                 this.selNodes.splice(index, 1);
33114             }
33115             delete this.selMap[node.id];
33116             this.fireEvent("selectionchange", this, this.selNodes);
33117         }
33118     },
33119     
33120     /**
33121      * Clear all selections
33122      */
33123     clearSelections : function(suppressEvent){
33124         var sn = this.selNodes;
33125         if(sn.length > 0){
33126             for(var i = 0, len = sn.length; i < len; i++){
33127                 sn[i].ui.onSelectedChange(false);
33128             }
33129             this.selNodes = [];
33130             this.selMap = {};
33131             if(suppressEvent !== true){
33132                 this.fireEvent("selectionchange", this, this.selNodes);
33133             }
33134         }
33135     },
33136     
33137     /**
33138      * Returns true if the node is selected
33139      * @param {TreeNode} node The node to check
33140      * @return {Boolean}
33141      */
33142     isSelected : function(node){
33143         return this.selMap[node.id] ? true : false;  
33144     },
33145     
33146     /**
33147      * Returns an array of the selected nodes
33148      * @return {Array}
33149      */
33150     getSelectedNodes : function(){
33151         return this.selNodes;    
33152     },
33153
33154     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33155
33156     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33157
33158     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33159 });/*
33160  * Based on:
33161  * Ext JS Library 1.1.1
33162  * Copyright(c) 2006-2007, Ext JS, LLC.
33163  *
33164  * Originally Released Under LGPL - original licence link has changed is not relivant.
33165  *
33166  * Fork - LGPL
33167  * <script type="text/javascript">
33168  */
33169  
33170 /**
33171  * @class Roo.tree.TreeNode
33172  * @extends Roo.data.Node
33173  * @cfg {String} text The text for this node
33174  * @cfg {Boolean} expanded true to start the node expanded
33175  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33176  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33177  * @cfg {Boolean} disabled true to start the node disabled
33178  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33179  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33180  * @cfg {String} cls A css class to be added to the node
33181  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33182  * @cfg {String} href URL of the link used for the node (defaults to #)
33183  * @cfg {String} hrefTarget target frame for the link
33184  * @cfg {String} qtip An Ext QuickTip for the node
33185  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33186  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33187  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33188  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33189  * (defaults to undefined with no checkbox rendered)
33190  * @constructor
33191  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33192  */
33193 Roo.tree.TreeNode = function(attributes){
33194     attributes = attributes || {};
33195     if(typeof attributes == "string"){
33196         attributes = {text: attributes};
33197     }
33198     this.childrenRendered = false;
33199     this.rendered = false;
33200     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33201     this.expanded = attributes.expanded === true;
33202     this.isTarget = attributes.isTarget !== false;
33203     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33204     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33205
33206     /**
33207      * Read-only. The text for this node. To change it use setText().
33208      * @type String
33209      */
33210     this.text = attributes.text;
33211     /**
33212      * True if this node is disabled.
33213      * @type Boolean
33214      */
33215     this.disabled = attributes.disabled === true;
33216
33217     this.addEvents({
33218         /**
33219         * @event textchange
33220         * Fires when the text for this node is changed
33221         * @param {Node} this This node
33222         * @param {String} text The new text
33223         * @param {String} oldText The old text
33224         */
33225         "textchange" : true,
33226         /**
33227         * @event beforeexpand
33228         * Fires before this node is expanded, return false to cancel.
33229         * @param {Node} this This node
33230         * @param {Boolean} deep
33231         * @param {Boolean} anim
33232         */
33233         "beforeexpand" : true,
33234         /**
33235         * @event beforecollapse
33236         * Fires before this node is collapsed, return false to cancel.
33237         * @param {Node} this This node
33238         * @param {Boolean} deep
33239         * @param {Boolean} anim
33240         */
33241         "beforecollapse" : true,
33242         /**
33243         * @event expand
33244         * Fires when this node is expanded
33245         * @param {Node} this This node
33246         */
33247         "expand" : true,
33248         /**
33249         * @event disabledchange
33250         * Fires when the disabled status of this node changes
33251         * @param {Node} this This node
33252         * @param {Boolean} disabled
33253         */
33254         "disabledchange" : true,
33255         /**
33256         * @event collapse
33257         * Fires when this node is collapsed
33258         * @param {Node} this This node
33259         */
33260         "collapse" : true,
33261         /**
33262         * @event beforeclick
33263         * Fires before click processing. Return false to cancel the default action.
33264         * @param {Node} this This node
33265         * @param {Roo.EventObject} e The event object
33266         */
33267         "beforeclick":true,
33268         /**
33269         * @event checkchange
33270         * Fires when a node with a checkbox's checked property changes
33271         * @param {Node} this This node
33272         * @param {Boolean} checked
33273         */
33274         "checkchange":true,
33275         /**
33276         * @event click
33277         * Fires when this node is clicked
33278         * @param {Node} this This node
33279         * @param {Roo.EventObject} e The event object
33280         */
33281         "click":true,
33282         /**
33283         * @event dblclick
33284         * Fires when this node is double clicked
33285         * @param {Node} this This node
33286         * @param {Roo.EventObject} e The event object
33287         */
33288         "dblclick":true,
33289         /**
33290         * @event contextmenu
33291         * Fires when this node is right clicked
33292         * @param {Node} this This node
33293         * @param {Roo.EventObject} e The event object
33294         */
33295         "contextmenu":true,
33296         /**
33297         * @event beforechildrenrendered
33298         * Fires right before the child nodes for this node are rendered
33299         * @param {Node} this This node
33300         */
33301         "beforechildrenrendered":true
33302     });
33303
33304     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33305
33306     /**
33307      * Read-only. The UI for this node
33308      * @type TreeNodeUI
33309      */
33310     this.ui = new uiClass(this);
33311     
33312     // finally support items[]
33313     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33314         return;
33315     }
33316     
33317     
33318     Roo.each(this.attributes.items, function(c) {
33319         this.appendChild(Roo.factory(c,Roo.Tree));
33320     }, this);
33321     delete this.attributes.items;
33322     
33323     
33324     
33325 };
33326 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33327     preventHScroll: true,
33328     /**
33329      * Returns true if this node is expanded
33330      * @return {Boolean}
33331      */
33332     isExpanded : function(){
33333         return this.expanded;
33334     },
33335
33336     /**
33337      * Returns the UI object for this node
33338      * @return {TreeNodeUI}
33339      */
33340     getUI : function(){
33341         return this.ui;
33342     },
33343
33344     // private override
33345     setFirstChild : function(node){
33346         var of = this.firstChild;
33347         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33348         if(this.childrenRendered && of && node != of){
33349             of.renderIndent(true, true);
33350         }
33351         if(this.rendered){
33352             this.renderIndent(true, true);
33353         }
33354     },
33355
33356     // private override
33357     setLastChild : function(node){
33358         var ol = this.lastChild;
33359         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33360         if(this.childrenRendered && ol && node != ol){
33361             ol.renderIndent(true, true);
33362         }
33363         if(this.rendered){
33364             this.renderIndent(true, true);
33365         }
33366     },
33367
33368     // these methods are overridden to provide lazy rendering support
33369     // private override
33370     appendChild : function()
33371     {
33372         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33373         if(node && this.childrenRendered){
33374             node.render();
33375         }
33376         this.ui.updateExpandIcon();
33377         return node;
33378     },
33379
33380     // private override
33381     removeChild : function(node){
33382         this.ownerTree.getSelectionModel().unselect(node);
33383         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33384         // if it's been rendered remove dom node
33385         if(this.childrenRendered){
33386             node.ui.remove();
33387         }
33388         if(this.childNodes.length < 1){
33389             this.collapse(false, false);
33390         }else{
33391             this.ui.updateExpandIcon();
33392         }
33393         if(!this.firstChild) {
33394             this.childrenRendered = false;
33395         }
33396         return node;
33397     },
33398
33399     // private override
33400     insertBefore : function(node, refNode){
33401         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33402         if(newNode && refNode && this.childrenRendered){
33403             node.render();
33404         }
33405         this.ui.updateExpandIcon();
33406         return newNode;
33407     },
33408
33409     /**
33410      * Sets the text for this node
33411      * @param {String} text
33412      */
33413     setText : function(text){
33414         var oldText = this.text;
33415         this.text = text;
33416         this.attributes.text = text;
33417         if(this.rendered){ // event without subscribing
33418             this.ui.onTextChange(this, text, oldText);
33419         }
33420         this.fireEvent("textchange", this, text, oldText);
33421     },
33422
33423     /**
33424      * Triggers selection of this node
33425      */
33426     select : function(){
33427         this.getOwnerTree().getSelectionModel().select(this);
33428     },
33429
33430     /**
33431      * Triggers deselection of this node
33432      */
33433     unselect : function(){
33434         this.getOwnerTree().getSelectionModel().unselect(this);
33435     },
33436
33437     /**
33438      * Returns true if this node is selected
33439      * @return {Boolean}
33440      */
33441     isSelected : function(){
33442         return this.getOwnerTree().getSelectionModel().isSelected(this);
33443     },
33444
33445     /**
33446      * Expand this node.
33447      * @param {Boolean} deep (optional) True to expand all children as well
33448      * @param {Boolean} anim (optional) false to cancel the default animation
33449      * @param {Function} callback (optional) A callback to be called when
33450      * expanding this node completes (does not wait for deep expand to complete).
33451      * Called with 1 parameter, this node.
33452      */
33453     expand : function(deep, anim, callback){
33454         if(!this.expanded){
33455             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33456                 return;
33457             }
33458             if(!this.childrenRendered){
33459                 this.renderChildren();
33460             }
33461             this.expanded = true;
33462             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33463                 this.ui.animExpand(function(){
33464                     this.fireEvent("expand", this);
33465                     if(typeof callback == "function"){
33466                         callback(this);
33467                     }
33468                     if(deep === true){
33469                         this.expandChildNodes(true);
33470                     }
33471                 }.createDelegate(this));
33472                 return;
33473             }else{
33474                 this.ui.expand();
33475                 this.fireEvent("expand", this);
33476                 if(typeof callback == "function"){
33477                     callback(this);
33478                 }
33479             }
33480         }else{
33481            if(typeof callback == "function"){
33482                callback(this);
33483            }
33484         }
33485         if(deep === true){
33486             this.expandChildNodes(true);
33487         }
33488     },
33489
33490     isHiddenRoot : function(){
33491         return this.isRoot && !this.getOwnerTree().rootVisible;
33492     },
33493
33494     /**
33495      * Collapse this node.
33496      * @param {Boolean} deep (optional) True to collapse all children as well
33497      * @param {Boolean} anim (optional) false to cancel the default animation
33498      */
33499     collapse : function(deep, anim){
33500         if(this.expanded && !this.isHiddenRoot()){
33501             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33502                 return;
33503             }
33504             this.expanded = false;
33505             if((this.getOwnerTree().animate && anim !== false) || anim){
33506                 this.ui.animCollapse(function(){
33507                     this.fireEvent("collapse", this);
33508                     if(deep === true){
33509                         this.collapseChildNodes(true);
33510                     }
33511                 }.createDelegate(this));
33512                 return;
33513             }else{
33514                 this.ui.collapse();
33515                 this.fireEvent("collapse", this);
33516             }
33517         }
33518         if(deep === true){
33519             var cs = this.childNodes;
33520             for(var i = 0, len = cs.length; i < len; i++) {
33521                 cs[i].collapse(true, false);
33522             }
33523         }
33524     },
33525
33526     // private
33527     delayedExpand : function(delay){
33528         if(!this.expandProcId){
33529             this.expandProcId = this.expand.defer(delay, this);
33530         }
33531     },
33532
33533     // private
33534     cancelExpand : function(){
33535         if(this.expandProcId){
33536             clearTimeout(this.expandProcId);
33537         }
33538         this.expandProcId = false;
33539     },
33540
33541     /**
33542      * Toggles expanded/collapsed state of the node
33543      */
33544     toggle : function(){
33545         if(this.expanded){
33546             this.collapse();
33547         }else{
33548             this.expand();
33549         }
33550     },
33551
33552     /**
33553      * Ensures all parent nodes are expanded
33554      */
33555     ensureVisible : function(callback){
33556         var tree = this.getOwnerTree();
33557         tree.expandPath(this.parentNode.getPath(), false, function(){
33558             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33559             Roo.callback(callback);
33560         }.createDelegate(this));
33561     },
33562
33563     /**
33564      * Expand all child nodes
33565      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33566      */
33567     expandChildNodes : function(deep){
33568         var cs = this.childNodes;
33569         for(var i = 0, len = cs.length; i < len; i++) {
33570                 cs[i].expand(deep);
33571         }
33572     },
33573
33574     /**
33575      * Collapse all child nodes
33576      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33577      */
33578     collapseChildNodes : function(deep){
33579         var cs = this.childNodes;
33580         for(var i = 0, len = cs.length; i < len; i++) {
33581                 cs[i].collapse(deep);
33582         }
33583     },
33584
33585     /**
33586      * Disables this node
33587      */
33588     disable : function(){
33589         this.disabled = true;
33590         this.unselect();
33591         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33592             this.ui.onDisableChange(this, true);
33593         }
33594         this.fireEvent("disabledchange", this, true);
33595     },
33596
33597     /**
33598      * Enables this node
33599      */
33600     enable : function(){
33601         this.disabled = false;
33602         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33603             this.ui.onDisableChange(this, false);
33604         }
33605         this.fireEvent("disabledchange", this, false);
33606     },
33607
33608     // private
33609     renderChildren : function(suppressEvent){
33610         if(suppressEvent !== false){
33611             this.fireEvent("beforechildrenrendered", this);
33612         }
33613         var cs = this.childNodes;
33614         for(var i = 0, len = cs.length; i < len; i++){
33615             cs[i].render(true);
33616         }
33617         this.childrenRendered = true;
33618     },
33619
33620     // private
33621     sort : function(fn, scope){
33622         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33623         if(this.childrenRendered){
33624             var cs = this.childNodes;
33625             for(var i = 0, len = cs.length; i < len; i++){
33626                 cs[i].render(true);
33627             }
33628         }
33629     },
33630
33631     // private
33632     render : function(bulkRender){
33633         this.ui.render(bulkRender);
33634         if(!this.rendered){
33635             this.rendered = true;
33636             if(this.expanded){
33637                 this.expanded = false;
33638                 this.expand(false, false);
33639             }
33640         }
33641     },
33642
33643     // private
33644     renderIndent : function(deep, refresh){
33645         if(refresh){
33646             this.ui.childIndent = null;
33647         }
33648         this.ui.renderIndent();
33649         if(deep === true && this.childrenRendered){
33650             var cs = this.childNodes;
33651             for(var i = 0, len = cs.length; i < len; i++){
33652                 cs[i].renderIndent(true, refresh);
33653             }
33654         }
33655     }
33656 });/*
33657  * Based on:
33658  * Ext JS Library 1.1.1
33659  * Copyright(c) 2006-2007, Ext JS, LLC.
33660  *
33661  * Originally Released Under LGPL - original licence link has changed is not relivant.
33662  *
33663  * Fork - LGPL
33664  * <script type="text/javascript">
33665  */
33666  
33667 /**
33668  * @class Roo.tree.AsyncTreeNode
33669  * @extends Roo.tree.TreeNode
33670  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33671  * @constructor
33672  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33673  */
33674  Roo.tree.AsyncTreeNode = function(config){
33675     this.loaded = false;
33676     this.loading = false;
33677     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33678     /**
33679     * @event beforeload
33680     * Fires before this node is loaded, return false to cancel
33681     * @param {Node} this This node
33682     */
33683     this.addEvents({'beforeload':true, 'load': true});
33684     /**
33685     * @event load
33686     * Fires when this node is loaded
33687     * @param {Node} this This node
33688     */
33689     /**
33690      * The loader used by this node (defaults to using the tree's defined loader)
33691      * @type TreeLoader
33692      * @property loader
33693      */
33694 };
33695 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33696     expand : function(deep, anim, callback){
33697         if(this.loading){ // if an async load is already running, waiting til it's done
33698             var timer;
33699             var f = function(){
33700                 if(!this.loading){ // done loading
33701                     clearInterval(timer);
33702                     this.expand(deep, anim, callback);
33703                 }
33704             }.createDelegate(this);
33705             timer = setInterval(f, 200);
33706             return;
33707         }
33708         if(!this.loaded){
33709             if(this.fireEvent("beforeload", this) === false){
33710                 return;
33711             }
33712             this.loading = true;
33713             this.ui.beforeLoad(this);
33714             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33715             if(loader){
33716                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33717                 return;
33718             }
33719         }
33720         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33721     },
33722     
33723     /**
33724      * Returns true if this node is currently loading
33725      * @return {Boolean}
33726      */
33727     isLoading : function(){
33728         return this.loading;  
33729     },
33730     
33731     loadComplete : function(deep, anim, callback){
33732         this.loading = false;
33733         this.loaded = true;
33734         this.ui.afterLoad(this);
33735         this.fireEvent("load", this);
33736         this.expand(deep, anim, callback);
33737     },
33738     
33739     /**
33740      * Returns true if this node has been loaded
33741      * @return {Boolean}
33742      */
33743     isLoaded : function(){
33744         return this.loaded;
33745     },
33746     
33747     hasChildNodes : function(){
33748         if(!this.isLeaf() && !this.loaded){
33749             return true;
33750         }else{
33751             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33752         }
33753     },
33754
33755     /**
33756      * Trigger a reload for this node
33757      * @param {Function} callback
33758      */
33759     reload : function(callback){
33760         this.collapse(false, false);
33761         while(this.firstChild){
33762             this.removeChild(this.firstChild);
33763         }
33764         this.childrenRendered = false;
33765         this.loaded = false;
33766         if(this.isHiddenRoot()){
33767             this.expanded = false;
33768         }
33769         this.expand(false, false, callback);
33770     }
33771 });/*
33772  * Based on:
33773  * Ext JS Library 1.1.1
33774  * Copyright(c) 2006-2007, Ext JS, LLC.
33775  *
33776  * Originally Released Under LGPL - original licence link has changed is not relivant.
33777  *
33778  * Fork - LGPL
33779  * <script type="text/javascript">
33780  */
33781  
33782 /**
33783  * @class Roo.tree.TreeNodeUI
33784  * @constructor
33785  * @param {Object} node The node to render
33786  * The TreeNode UI implementation is separate from the
33787  * tree implementation. Unless you are customizing the tree UI,
33788  * you should never have to use this directly.
33789  */
33790 Roo.tree.TreeNodeUI = function(node){
33791     this.node = node;
33792     this.rendered = false;
33793     this.animating = false;
33794     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33795 };
33796
33797 Roo.tree.TreeNodeUI.prototype = {
33798     removeChild : function(node){
33799         if(this.rendered){
33800             this.ctNode.removeChild(node.ui.getEl());
33801         }
33802     },
33803
33804     beforeLoad : function(){
33805          this.addClass("x-tree-node-loading");
33806     },
33807
33808     afterLoad : function(){
33809          this.removeClass("x-tree-node-loading");
33810     },
33811
33812     onTextChange : function(node, text, oldText){
33813         if(this.rendered){
33814             this.textNode.innerHTML = text;
33815         }
33816     },
33817
33818     onDisableChange : function(node, state){
33819         this.disabled = state;
33820         if(state){
33821             this.addClass("x-tree-node-disabled");
33822         }else{
33823             this.removeClass("x-tree-node-disabled");
33824         }
33825     },
33826
33827     onSelectedChange : function(state){
33828         if(state){
33829             this.focus();
33830             this.addClass("x-tree-selected");
33831         }else{
33832             //this.blur();
33833             this.removeClass("x-tree-selected");
33834         }
33835     },
33836
33837     onMove : function(tree, node, oldParent, newParent, index, refNode){
33838         this.childIndent = null;
33839         if(this.rendered){
33840             var targetNode = newParent.ui.getContainer();
33841             if(!targetNode){//target not rendered
33842                 this.holder = document.createElement("div");
33843                 this.holder.appendChild(this.wrap);
33844                 return;
33845             }
33846             var insertBefore = refNode ? refNode.ui.getEl() : null;
33847             if(insertBefore){
33848                 targetNode.insertBefore(this.wrap, insertBefore);
33849             }else{
33850                 targetNode.appendChild(this.wrap);
33851             }
33852             this.node.renderIndent(true);
33853         }
33854     },
33855
33856     addClass : function(cls){
33857         if(this.elNode){
33858             Roo.fly(this.elNode).addClass(cls);
33859         }
33860     },
33861
33862     removeClass : function(cls){
33863         if(this.elNode){
33864             Roo.fly(this.elNode).removeClass(cls);
33865         }
33866     },
33867
33868     remove : function(){
33869         if(this.rendered){
33870             this.holder = document.createElement("div");
33871             this.holder.appendChild(this.wrap);
33872         }
33873     },
33874
33875     fireEvent : function(){
33876         return this.node.fireEvent.apply(this.node, arguments);
33877     },
33878
33879     initEvents : function(){
33880         this.node.on("move", this.onMove, this);
33881         var E = Roo.EventManager;
33882         var a = this.anchor;
33883
33884         var el = Roo.fly(a, '_treeui');
33885
33886         if(Roo.isOpera){ // opera render bug ignores the CSS
33887             el.setStyle("text-decoration", "none");
33888         }
33889
33890         el.on("click", this.onClick, this);
33891         el.on("dblclick", this.onDblClick, this);
33892
33893         if(this.checkbox){
33894             Roo.EventManager.on(this.checkbox,
33895                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33896         }
33897
33898         el.on("contextmenu", this.onContextMenu, this);
33899
33900         var icon = Roo.fly(this.iconNode);
33901         icon.on("click", this.onClick, this);
33902         icon.on("dblclick", this.onDblClick, this);
33903         icon.on("contextmenu", this.onContextMenu, this);
33904         E.on(this.ecNode, "click", this.ecClick, this, true);
33905
33906         if(this.node.disabled){
33907             this.addClass("x-tree-node-disabled");
33908         }
33909         if(this.node.hidden){
33910             this.addClass("x-tree-node-disabled");
33911         }
33912         var ot = this.node.getOwnerTree();
33913         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33914         if(dd && (!this.node.isRoot || ot.rootVisible)){
33915             Roo.dd.Registry.register(this.elNode, {
33916                 node: this.node,
33917                 handles: this.getDDHandles(),
33918                 isHandle: false
33919             });
33920         }
33921     },
33922
33923     getDDHandles : function(){
33924         return [this.iconNode, this.textNode];
33925     },
33926
33927     hide : function(){
33928         if(this.rendered){
33929             this.wrap.style.display = "none";
33930         }
33931     },
33932
33933     show : function(){
33934         if(this.rendered){
33935             this.wrap.style.display = "";
33936         }
33937     },
33938
33939     onContextMenu : function(e){
33940         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33941             e.preventDefault();
33942             this.focus();
33943             this.fireEvent("contextmenu", this.node, e);
33944         }
33945     },
33946
33947     onClick : function(e){
33948         if(this.dropping){
33949             e.stopEvent();
33950             return;
33951         }
33952         if(this.fireEvent("beforeclick", this.node, e) !== false){
33953             if(!this.disabled && this.node.attributes.href){
33954                 this.fireEvent("click", this.node, e);
33955                 return;
33956             }
33957             e.preventDefault();
33958             if(this.disabled){
33959                 return;
33960             }
33961
33962             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33963                 this.node.toggle();
33964             }
33965
33966             this.fireEvent("click", this.node, e);
33967         }else{
33968             e.stopEvent();
33969         }
33970     },
33971
33972     onDblClick : function(e){
33973         e.preventDefault();
33974         if(this.disabled){
33975             return;
33976         }
33977         if(this.checkbox){
33978             this.toggleCheck();
33979         }
33980         if(!this.animating && this.node.hasChildNodes()){
33981             this.node.toggle();
33982         }
33983         this.fireEvent("dblclick", this.node, e);
33984     },
33985
33986     onCheckChange : function(){
33987         var checked = this.checkbox.checked;
33988         this.node.attributes.checked = checked;
33989         this.fireEvent('checkchange', this.node, checked);
33990     },
33991
33992     ecClick : function(e){
33993         if(!this.animating && this.node.hasChildNodes()){
33994             this.node.toggle();
33995         }
33996     },
33997
33998     startDrop : function(){
33999         this.dropping = true;
34000     },
34001
34002     // delayed drop so the click event doesn't get fired on a drop
34003     endDrop : function(){
34004        setTimeout(function(){
34005            this.dropping = false;
34006        }.createDelegate(this), 50);
34007     },
34008
34009     expand : function(){
34010         this.updateExpandIcon();
34011         this.ctNode.style.display = "";
34012     },
34013
34014     focus : function(){
34015         if(!this.node.preventHScroll){
34016             try{this.anchor.focus();
34017             }catch(e){}
34018         }else if(!Roo.isIE){
34019             try{
34020                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34021                 var l = noscroll.scrollLeft;
34022                 this.anchor.focus();
34023                 noscroll.scrollLeft = l;
34024             }catch(e){}
34025         }
34026     },
34027
34028     toggleCheck : function(value){
34029         var cb = this.checkbox;
34030         if(cb){
34031             cb.checked = (value === undefined ? !cb.checked : value);
34032         }
34033     },
34034
34035     blur : function(){
34036         try{
34037             this.anchor.blur();
34038         }catch(e){}
34039     },
34040
34041     animExpand : function(callback){
34042         var ct = Roo.get(this.ctNode);
34043         ct.stopFx();
34044         if(!this.node.hasChildNodes()){
34045             this.updateExpandIcon();
34046             this.ctNode.style.display = "";
34047             Roo.callback(callback);
34048             return;
34049         }
34050         this.animating = true;
34051         this.updateExpandIcon();
34052
34053         ct.slideIn('t', {
34054            callback : function(){
34055                this.animating = false;
34056                Roo.callback(callback);
34057             },
34058             scope: this,
34059             duration: this.node.ownerTree.duration || .25
34060         });
34061     },
34062
34063     highlight : function(){
34064         var tree = this.node.getOwnerTree();
34065         Roo.fly(this.wrap).highlight(
34066             tree.hlColor || "C3DAF9",
34067             {endColor: tree.hlBaseColor}
34068         );
34069     },
34070
34071     collapse : function(){
34072         this.updateExpandIcon();
34073         this.ctNode.style.display = "none";
34074     },
34075
34076     animCollapse : function(callback){
34077         var ct = Roo.get(this.ctNode);
34078         ct.enableDisplayMode('block');
34079         ct.stopFx();
34080
34081         this.animating = true;
34082         this.updateExpandIcon();
34083
34084         ct.slideOut('t', {
34085             callback : function(){
34086                this.animating = false;
34087                Roo.callback(callback);
34088             },
34089             scope: this,
34090             duration: this.node.ownerTree.duration || .25
34091         });
34092     },
34093
34094     getContainer : function(){
34095         return this.ctNode;
34096     },
34097
34098     getEl : function(){
34099         return this.wrap;
34100     },
34101
34102     appendDDGhost : function(ghostNode){
34103         ghostNode.appendChild(this.elNode.cloneNode(true));
34104     },
34105
34106     getDDRepairXY : function(){
34107         return Roo.lib.Dom.getXY(this.iconNode);
34108     },
34109
34110     onRender : function(){
34111         this.render();
34112     },
34113
34114     render : function(bulkRender){
34115         var n = this.node, a = n.attributes;
34116         var targetNode = n.parentNode ?
34117               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34118
34119         if(!this.rendered){
34120             this.rendered = true;
34121
34122             this.renderElements(n, a, targetNode, bulkRender);
34123
34124             if(a.qtip){
34125                if(this.textNode.setAttributeNS){
34126                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34127                    if(a.qtipTitle){
34128                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34129                    }
34130                }else{
34131                    this.textNode.setAttribute("ext:qtip", a.qtip);
34132                    if(a.qtipTitle){
34133                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34134                    }
34135                }
34136             }else if(a.qtipCfg){
34137                 a.qtipCfg.target = Roo.id(this.textNode);
34138                 Roo.QuickTips.register(a.qtipCfg);
34139             }
34140             this.initEvents();
34141             if(!this.node.expanded){
34142                 this.updateExpandIcon();
34143             }
34144         }else{
34145             if(bulkRender === true) {
34146                 targetNode.appendChild(this.wrap);
34147             }
34148         }
34149     },
34150
34151     renderElements : function(n, a, targetNode, bulkRender)
34152     {
34153         // add some indent caching, this helps performance when rendering a large tree
34154         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34155         var t = n.getOwnerTree();
34156         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34157         if (typeof(n.attributes.html) != 'undefined') {
34158             txt = n.attributes.html;
34159         }
34160         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34161         var cb = typeof a.checked == 'boolean';
34162         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34163         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34164             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34165             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34166             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34167             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34168             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34169              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34170                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34171             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34172             "</li>"];
34173
34174         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34175             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34176                                 n.nextSibling.ui.getEl(), buf.join(""));
34177         }else{
34178             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34179         }
34180
34181         this.elNode = this.wrap.childNodes[0];
34182         this.ctNode = this.wrap.childNodes[1];
34183         var cs = this.elNode.childNodes;
34184         this.indentNode = cs[0];
34185         this.ecNode = cs[1];
34186         this.iconNode = cs[2];
34187         var index = 3;
34188         if(cb){
34189             this.checkbox = cs[3];
34190             index++;
34191         }
34192         this.anchor = cs[index];
34193         this.textNode = cs[index].firstChild;
34194     },
34195
34196     getAnchor : function(){
34197         return this.anchor;
34198     },
34199
34200     getTextEl : function(){
34201         return this.textNode;
34202     },
34203
34204     getIconEl : function(){
34205         return this.iconNode;
34206     },
34207
34208     isChecked : function(){
34209         return this.checkbox ? this.checkbox.checked : false;
34210     },
34211
34212     updateExpandIcon : function(){
34213         if(this.rendered){
34214             var n = this.node, c1, c2;
34215             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34216             var hasChild = n.hasChildNodes();
34217             if(hasChild){
34218                 if(n.expanded){
34219                     cls += "-minus";
34220                     c1 = "x-tree-node-collapsed";
34221                     c2 = "x-tree-node-expanded";
34222                 }else{
34223                     cls += "-plus";
34224                     c1 = "x-tree-node-expanded";
34225                     c2 = "x-tree-node-collapsed";
34226                 }
34227                 if(this.wasLeaf){
34228                     this.removeClass("x-tree-node-leaf");
34229                     this.wasLeaf = false;
34230                 }
34231                 if(this.c1 != c1 || this.c2 != c2){
34232                     Roo.fly(this.elNode).replaceClass(c1, c2);
34233                     this.c1 = c1; this.c2 = c2;
34234                 }
34235             }else{
34236                 // this changes non-leafs into leafs if they have no children.
34237                 // it's not very rational behaviour..
34238                 
34239                 if(!this.wasLeaf && this.node.leaf){
34240                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34241                     delete this.c1;
34242                     delete this.c2;
34243                     this.wasLeaf = true;
34244                 }
34245             }
34246             var ecc = "x-tree-ec-icon "+cls;
34247             if(this.ecc != ecc){
34248                 this.ecNode.className = ecc;
34249                 this.ecc = ecc;
34250             }
34251         }
34252     },
34253
34254     getChildIndent : function(){
34255         if(!this.childIndent){
34256             var buf = [];
34257             var p = this.node;
34258             while(p){
34259                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34260                     if(!p.isLast()) {
34261                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34262                     } else {
34263                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34264                     }
34265                 }
34266                 p = p.parentNode;
34267             }
34268             this.childIndent = buf.join("");
34269         }
34270         return this.childIndent;
34271     },
34272
34273     renderIndent : function(){
34274         if(this.rendered){
34275             var indent = "";
34276             var p = this.node.parentNode;
34277             if(p){
34278                 indent = p.ui.getChildIndent();
34279             }
34280             if(this.indentMarkup != indent){ // don't rerender if not required
34281                 this.indentNode.innerHTML = indent;
34282                 this.indentMarkup = indent;
34283             }
34284             this.updateExpandIcon();
34285         }
34286     }
34287 };
34288
34289 Roo.tree.RootTreeNodeUI = function(){
34290     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34291 };
34292 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34293     render : function(){
34294         if(!this.rendered){
34295             var targetNode = this.node.ownerTree.innerCt.dom;
34296             this.node.expanded = true;
34297             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34298             this.wrap = this.ctNode = targetNode.firstChild;
34299         }
34300     },
34301     collapse : function(){
34302     },
34303     expand : function(){
34304     }
34305 });/*
34306  * Based on:
34307  * Ext JS Library 1.1.1
34308  * Copyright(c) 2006-2007, Ext JS, LLC.
34309  *
34310  * Originally Released Under LGPL - original licence link has changed is not relivant.
34311  *
34312  * Fork - LGPL
34313  * <script type="text/javascript">
34314  */
34315 /**
34316  * @class Roo.tree.TreeLoader
34317  * @extends Roo.util.Observable
34318  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34319  * nodes from a specified URL. The response must be a javascript Array definition
34320  * who's elements are node definition objects. eg:
34321  * <pre><code>
34322 {  success : true,
34323    data :      [
34324    
34325     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34326     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34327     ]
34328 }
34329
34330
34331 </code></pre>
34332  * <br><br>
34333  * The old style respose with just an array is still supported, but not recommended.
34334  * <br><br>
34335  *
34336  * A server request is sent, and child nodes are loaded only when a node is expanded.
34337  * The loading node's id is passed to the server under the parameter name "node" to
34338  * enable the server to produce the correct child nodes.
34339  * <br><br>
34340  * To pass extra parameters, an event handler may be attached to the "beforeload"
34341  * event, and the parameters specified in the TreeLoader's baseParams property:
34342  * <pre><code>
34343     myTreeLoader.on("beforeload", function(treeLoader, node) {
34344         this.baseParams.category = node.attributes.category;
34345     }, this);
34346 </code></pre><
34347  * This would pass an HTTP parameter called "category" to the server containing
34348  * the value of the Node's "category" attribute.
34349  * @constructor
34350  * Creates a new Treeloader.
34351  * @param {Object} config A config object containing config properties.
34352  */
34353 Roo.tree.TreeLoader = function(config){
34354     this.baseParams = {};
34355     this.requestMethod = "POST";
34356     Roo.apply(this, config);
34357
34358     this.addEvents({
34359     
34360         /**
34361          * @event beforeload
34362          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34363          * @param {Object} This TreeLoader object.
34364          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34365          * @param {Object} callback The callback function specified in the {@link #load} call.
34366          */
34367         beforeload : true,
34368         /**
34369          * @event load
34370          * Fires when the node has been successfuly loaded.
34371          * @param {Object} This TreeLoader object.
34372          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34373          * @param {Object} response The response object containing the data from the server.
34374          */
34375         load : true,
34376         /**
34377          * @event loadexception
34378          * Fires if the network request failed.
34379          * @param {Object} This TreeLoader object.
34380          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34381          * @param {Object} response The response object containing the data from the server.
34382          */
34383         loadexception : true,
34384         /**
34385          * @event create
34386          * Fires before a node is created, enabling you to return custom Node types 
34387          * @param {Object} This TreeLoader object.
34388          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34389          */
34390         create : true
34391     });
34392
34393     Roo.tree.TreeLoader.superclass.constructor.call(this);
34394 };
34395
34396 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34397     /**
34398     * @cfg {String} dataUrl The URL from which to request a Json string which
34399     * specifies an array of node definition object representing the child nodes
34400     * to be loaded.
34401     */
34402     /**
34403     * @cfg {String} requestMethod either GET or POST
34404     * defaults to POST (due to BC)
34405     * to be loaded.
34406     */
34407     /**
34408     * @cfg {Object} baseParams (optional) An object containing properties which
34409     * specify HTTP parameters to be passed to each request for child nodes.
34410     */
34411     /**
34412     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34413     * created by this loader. If the attributes sent by the server have an attribute in this object,
34414     * they take priority.
34415     */
34416     /**
34417     * @cfg {Object} uiProviders (optional) An object containing properties which
34418     * 
34419     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34420     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34421     * <i>uiProvider</i> attribute of a returned child node is a string rather
34422     * than a reference to a TreeNodeUI implementation, this that string value
34423     * is used as a property name in the uiProviders object. You can define the provider named
34424     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34425     */
34426     uiProviders : {},
34427
34428     /**
34429     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34430     * child nodes before loading.
34431     */
34432     clearOnLoad : true,
34433
34434     /**
34435     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34436     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34437     * Grid query { data : [ .....] }
34438     */
34439     
34440     root : false,
34441      /**
34442     * @cfg {String} queryParam (optional) 
34443     * Name of the query as it will be passed on the querystring (defaults to 'node')
34444     * eg. the request will be ?node=[id]
34445     */
34446     
34447     
34448     queryParam: false,
34449     
34450     /**
34451      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34452      * This is called automatically when a node is expanded, but may be used to reload
34453      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34454      * @param {Roo.tree.TreeNode} node
34455      * @param {Function} callback
34456      */
34457     load : function(node, callback){
34458         if(this.clearOnLoad){
34459             while(node.firstChild){
34460                 node.removeChild(node.firstChild);
34461             }
34462         }
34463         if(node.attributes.children){ // preloaded json children
34464             var cs = node.attributes.children;
34465             for(var i = 0, len = cs.length; i < len; i++){
34466                 node.appendChild(this.createNode(cs[i]));
34467             }
34468             if(typeof callback == "function"){
34469                 callback();
34470             }
34471         }else if(this.dataUrl){
34472             this.requestData(node, callback);
34473         }
34474     },
34475
34476     getParams: function(node){
34477         var buf = [], bp = this.baseParams;
34478         for(var key in bp){
34479             if(typeof bp[key] != "function"){
34480                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34481             }
34482         }
34483         var n = this.queryParam === false ? 'node' : this.queryParam;
34484         buf.push(n + "=", encodeURIComponent(node.id));
34485         return buf.join("");
34486     },
34487
34488     requestData : function(node, callback){
34489         if(this.fireEvent("beforeload", this, node, callback) !== false){
34490             this.transId = Roo.Ajax.request({
34491                 method:this.requestMethod,
34492                 url: this.dataUrl||this.url,
34493                 success: this.handleResponse,
34494                 failure: this.handleFailure,
34495                 scope: this,
34496                 argument: {callback: callback, node: node},
34497                 params: this.getParams(node)
34498             });
34499         }else{
34500             // if the load is cancelled, make sure we notify
34501             // the node that we are done
34502             if(typeof callback == "function"){
34503                 callback();
34504             }
34505         }
34506     },
34507
34508     isLoading : function(){
34509         return this.transId ? true : false;
34510     },
34511
34512     abort : function(){
34513         if(this.isLoading()){
34514             Roo.Ajax.abort(this.transId);
34515         }
34516     },
34517
34518     // private
34519     createNode : function(attr)
34520     {
34521         // apply baseAttrs, nice idea Corey!
34522         if(this.baseAttrs){
34523             Roo.applyIf(attr, this.baseAttrs);
34524         }
34525         if(this.applyLoader !== false){
34526             attr.loader = this;
34527         }
34528         // uiProvider = depreciated..
34529         
34530         if(typeof(attr.uiProvider) == 'string'){
34531            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34532                 /**  eval:var:attr */ eval(attr.uiProvider);
34533         }
34534         if(typeof(this.uiProviders['default']) != 'undefined') {
34535             attr.uiProvider = this.uiProviders['default'];
34536         }
34537         
34538         this.fireEvent('create', this, attr);
34539         
34540         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34541         return(attr.leaf ?
34542                         new Roo.tree.TreeNode(attr) :
34543                         new Roo.tree.AsyncTreeNode(attr));
34544     },
34545
34546     processResponse : function(response, node, callback)
34547     {
34548         var json = response.responseText;
34549         try {
34550             
34551             var o = Roo.decode(json);
34552             
34553             if (this.root === false && typeof(o.success) != undefined) {
34554                 this.root = 'data'; // the default behaviour for list like data..
34555                 }
34556                 
34557             if (this.root !== false &&  !o.success) {
34558                 // it's a failure condition.
34559                 var a = response.argument;
34560                 this.fireEvent("loadexception", this, a.node, response);
34561                 Roo.log("Load failed - should have a handler really");
34562                 return;
34563             }
34564             
34565             
34566             
34567             if (this.root !== false) {
34568                  o = o[this.root];
34569             }
34570             
34571             for(var i = 0, len = o.length; i < len; i++){
34572                 var n = this.createNode(o[i]);
34573                 if(n){
34574                     node.appendChild(n);
34575                 }
34576             }
34577             if(typeof callback == "function"){
34578                 callback(this, node);
34579             }
34580         }catch(e){
34581             this.handleFailure(response);
34582         }
34583     },
34584
34585     handleResponse : function(response){
34586         this.transId = false;
34587         var a = response.argument;
34588         this.processResponse(response, a.node, a.callback);
34589         this.fireEvent("load", this, a.node, response);
34590     },
34591
34592     handleFailure : function(response)
34593     {
34594         // should handle failure better..
34595         this.transId = false;
34596         var a = response.argument;
34597         this.fireEvent("loadexception", this, a.node, response);
34598         if(typeof a.callback == "function"){
34599             a.callback(this, a.node);
34600         }
34601     }
34602 });/*
34603  * Based on:
34604  * Ext JS Library 1.1.1
34605  * Copyright(c) 2006-2007, Ext JS, LLC.
34606  *
34607  * Originally Released Under LGPL - original licence link has changed is not relivant.
34608  *
34609  * Fork - LGPL
34610  * <script type="text/javascript">
34611  */
34612
34613 /**
34614 * @class Roo.tree.TreeFilter
34615 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34616 * @param {TreePanel} tree
34617 * @param {Object} config (optional)
34618  */
34619 Roo.tree.TreeFilter = function(tree, config){
34620     this.tree = tree;
34621     this.filtered = {};
34622     Roo.apply(this, config);
34623 };
34624
34625 Roo.tree.TreeFilter.prototype = {
34626     clearBlank:false,
34627     reverse:false,
34628     autoClear:false,
34629     remove:false,
34630
34631      /**
34632      * Filter the data by a specific attribute.
34633      * @param {String/RegExp} value Either string that the attribute value
34634      * should start with or a RegExp to test against the attribute
34635      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34636      * @param {TreeNode} startNode (optional) The node to start the filter at.
34637      */
34638     filter : function(value, attr, startNode){
34639         attr = attr || "text";
34640         var f;
34641         if(typeof value == "string"){
34642             var vlen = value.length;
34643             // auto clear empty filter
34644             if(vlen == 0 && this.clearBlank){
34645                 this.clear();
34646                 return;
34647             }
34648             value = value.toLowerCase();
34649             f = function(n){
34650                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34651             };
34652         }else if(value.exec){ // regex?
34653             f = function(n){
34654                 return value.test(n.attributes[attr]);
34655             };
34656         }else{
34657             throw 'Illegal filter type, must be string or regex';
34658         }
34659         this.filterBy(f, null, startNode);
34660         },
34661
34662     /**
34663      * Filter by a function. The passed function will be called with each
34664      * node in the tree (or from the startNode). If the function returns true, the node is kept
34665      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34666      * @param {Function} fn The filter function
34667      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34668      */
34669     filterBy : function(fn, scope, startNode){
34670         startNode = startNode || this.tree.root;
34671         if(this.autoClear){
34672             this.clear();
34673         }
34674         var af = this.filtered, rv = this.reverse;
34675         var f = function(n){
34676             if(n == startNode){
34677                 return true;
34678             }
34679             if(af[n.id]){
34680                 return false;
34681             }
34682             var m = fn.call(scope || n, n);
34683             if(!m || rv){
34684                 af[n.id] = n;
34685                 n.ui.hide();
34686                 return false;
34687             }
34688             return true;
34689         };
34690         startNode.cascade(f);
34691         if(this.remove){
34692            for(var id in af){
34693                if(typeof id != "function"){
34694                    var n = af[id];
34695                    if(n && n.parentNode){
34696                        n.parentNode.removeChild(n);
34697                    }
34698                }
34699            }
34700         }
34701     },
34702
34703     /**
34704      * Clears the current filter. Note: with the "remove" option
34705      * set a filter cannot be cleared.
34706      */
34707     clear : function(){
34708         var t = this.tree;
34709         var af = this.filtered;
34710         for(var id in af){
34711             if(typeof id != "function"){
34712                 var n = af[id];
34713                 if(n){
34714                     n.ui.show();
34715                 }
34716             }
34717         }
34718         this.filtered = {};
34719     }
34720 };
34721 /*
34722  * Based on:
34723  * Ext JS Library 1.1.1
34724  * Copyright(c) 2006-2007, Ext JS, LLC.
34725  *
34726  * Originally Released Under LGPL - original licence link has changed is not relivant.
34727  *
34728  * Fork - LGPL
34729  * <script type="text/javascript">
34730  */
34731  
34732
34733 /**
34734  * @class Roo.tree.TreeSorter
34735  * Provides sorting of nodes in a TreePanel
34736  * 
34737  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34738  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34739  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34740  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34741  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34742  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34743  * @constructor
34744  * @param {TreePanel} tree
34745  * @param {Object} config
34746  */
34747 Roo.tree.TreeSorter = function(tree, config){
34748     Roo.apply(this, config);
34749     tree.on("beforechildrenrendered", this.doSort, this);
34750     tree.on("append", this.updateSort, this);
34751     tree.on("insert", this.updateSort, this);
34752     
34753     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34754     var p = this.property || "text";
34755     var sortType = this.sortType;
34756     var fs = this.folderSort;
34757     var cs = this.caseSensitive === true;
34758     var leafAttr = this.leafAttr || 'leaf';
34759
34760     this.sortFn = function(n1, n2){
34761         if(fs){
34762             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34763                 return 1;
34764             }
34765             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34766                 return -1;
34767             }
34768         }
34769         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34770         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34771         if(v1 < v2){
34772                         return dsc ? +1 : -1;
34773                 }else if(v1 > v2){
34774                         return dsc ? -1 : +1;
34775         }else{
34776                 return 0;
34777         }
34778     };
34779 };
34780
34781 Roo.tree.TreeSorter.prototype = {
34782     doSort : function(node){
34783         node.sort(this.sortFn);
34784     },
34785     
34786     compareNodes : function(n1, n2){
34787         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34788     },
34789     
34790     updateSort : function(tree, node){
34791         if(node.childrenRendered){
34792             this.doSort.defer(1, this, [node]);
34793         }
34794     }
34795 };/*
34796  * Based on:
34797  * Ext JS Library 1.1.1
34798  * Copyright(c) 2006-2007, Ext JS, LLC.
34799  *
34800  * Originally Released Under LGPL - original licence link has changed is not relivant.
34801  *
34802  * Fork - LGPL
34803  * <script type="text/javascript">
34804  */
34805
34806 if(Roo.dd.DropZone){
34807     
34808 Roo.tree.TreeDropZone = function(tree, config){
34809     this.allowParentInsert = false;
34810     this.allowContainerDrop = false;
34811     this.appendOnly = false;
34812     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34813     this.tree = tree;
34814     this.lastInsertClass = "x-tree-no-status";
34815     this.dragOverData = {};
34816 };
34817
34818 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34819     ddGroup : "TreeDD",
34820     scroll:  true,
34821     
34822     expandDelay : 1000,
34823     
34824     expandNode : function(node){
34825         if(node.hasChildNodes() && !node.isExpanded()){
34826             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34827         }
34828     },
34829     
34830     queueExpand : function(node){
34831         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34832     },
34833     
34834     cancelExpand : function(){
34835         if(this.expandProcId){
34836             clearTimeout(this.expandProcId);
34837             this.expandProcId = false;
34838         }
34839     },
34840     
34841     isValidDropPoint : function(n, pt, dd, e, data){
34842         if(!n || !data){ return false; }
34843         var targetNode = n.node;
34844         var dropNode = data.node;
34845         // default drop rules
34846         if(!(targetNode && targetNode.isTarget && pt)){
34847             return false;
34848         }
34849         if(pt == "append" && targetNode.allowChildren === false){
34850             return false;
34851         }
34852         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34853             return false;
34854         }
34855         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34856             return false;
34857         }
34858         // reuse the object
34859         var overEvent = this.dragOverData;
34860         overEvent.tree = this.tree;
34861         overEvent.target = targetNode;
34862         overEvent.data = data;
34863         overEvent.point = pt;
34864         overEvent.source = dd;
34865         overEvent.rawEvent = e;
34866         overEvent.dropNode = dropNode;
34867         overEvent.cancel = false;  
34868         var result = this.tree.fireEvent("nodedragover", overEvent);
34869         return overEvent.cancel === false && result !== false;
34870     },
34871     
34872     getDropPoint : function(e, n, dd)
34873     {
34874         var tn = n.node;
34875         if(tn.isRoot){
34876             return tn.allowChildren !== false ? "append" : false; // always append for root
34877         }
34878         var dragEl = n.ddel;
34879         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34880         var y = Roo.lib.Event.getPageY(e);
34881         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34882         
34883         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34884         var noAppend = tn.allowChildren === false;
34885         if(this.appendOnly || tn.parentNode.allowChildren === false){
34886             return noAppend ? false : "append";
34887         }
34888         var noBelow = false;
34889         if(!this.allowParentInsert){
34890             noBelow = tn.hasChildNodes() && tn.isExpanded();
34891         }
34892         var q = (b - t) / (noAppend ? 2 : 3);
34893         if(y >= t && y < (t + q)){
34894             return "above";
34895         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34896             return "below";
34897         }else{
34898             return "append";
34899         }
34900     },
34901     
34902     onNodeEnter : function(n, dd, e, data)
34903     {
34904         this.cancelExpand();
34905     },
34906     
34907     onNodeOver : function(n, dd, e, data)
34908     {
34909        
34910         var pt = this.getDropPoint(e, n, dd);
34911         var node = n.node;
34912         
34913         // auto node expand check
34914         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34915             this.queueExpand(node);
34916         }else if(pt != "append"){
34917             this.cancelExpand();
34918         }
34919         
34920         // set the insert point style on the target node
34921         var returnCls = this.dropNotAllowed;
34922         if(this.isValidDropPoint(n, pt, dd, e, data)){
34923            if(pt){
34924                var el = n.ddel;
34925                var cls;
34926                if(pt == "above"){
34927                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34928                    cls = "x-tree-drag-insert-above";
34929                }else if(pt == "below"){
34930                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34931                    cls = "x-tree-drag-insert-below";
34932                }else{
34933                    returnCls = "x-tree-drop-ok-append";
34934                    cls = "x-tree-drag-append";
34935                }
34936                if(this.lastInsertClass != cls){
34937                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34938                    this.lastInsertClass = cls;
34939                }
34940            }
34941        }
34942        return returnCls;
34943     },
34944     
34945     onNodeOut : function(n, dd, e, data){
34946         
34947         this.cancelExpand();
34948         this.removeDropIndicators(n);
34949     },
34950     
34951     onNodeDrop : function(n, dd, e, data){
34952         var point = this.getDropPoint(e, n, dd);
34953         var targetNode = n.node;
34954         targetNode.ui.startDrop();
34955         if(!this.isValidDropPoint(n, point, dd, e, data)){
34956             targetNode.ui.endDrop();
34957             return false;
34958         }
34959         // first try to find the drop node
34960         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34961         var dropEvent = {
34962             tree : this.tree,
34963             target: targetNode,
34964             data: data,
34965             point: point,
34966             source: dd,
34967             rawEvent: e,
34968             dropNode: dropNode,
34969             cancel: !dropNode   
34970         };
34971         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34972         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34973             targetNode.ui.endDrop();
34974             return false;
34975         }
34976         // allow target changing
34977         targetNode = dropEvent.target;
34978         if(point == "append" && !targetNode.isExpanded()){
34979             targetNode.expand(false, null, function(){
34980                 this.completeDrop(dropEvent);
34981             }.createDelegate(this));
34982         }else{
34983             this.completeDrop(dropEvent);
34984         }
34985         return true;
34986     },
34987     
34988     completeDrop : function(de){
34989         var ns = de.dropNode, p = de.point, t = de.target;
34990         if(!(ns instanceof Array)){
34991             ns = [ns];
34992         }
34993         var n;
34994         for(var i = 0, len = ns.length; i < len; i++){
34995             n = ns[i];
34996             if(p == "above"){
34997                 t.parentNode.insertBefore(n, t);
34998             }else if(p == "below"){
34999                 t.parentNode.insertBefore(n, t.nextSibling);
35000             }else{
35001                 t.appendChild(n);
35002             }
35003         }
35004         n.ui.focus();
35005         if(this.tree.hlDrop){
35006             n.ui.highlight();
35007         }
35008         t.ui.endDrop();
35009         this.tree.fireEvent("nodedrop", de);
35010     },
35011     
35012     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35013         if(this.tree.hlDrop){
35014             dropNode.ui.focus();
35015             dropNode.ui.highlight();
35016         }
35017         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35018     },
35019     
35020     getTree : function(){
35021         return this.tree;
35022     },
35023     
35024     removeDropIndicators : function(n){
35025         if(n && n.ddel){
35026             var el = n.ddel;
35027             Roo.fly(el).removeClass([
35028                     "x-tree-drag-insert-above",
35029                     "x-tree-drag-insert-below",
35030                     "x-tree-drag-append"]);
35031             this.lastInsertClass = "_noclass";
35032         }
35033     },
35034     
35035     beforeDragDrop : function(target, e, id){
35036         this.cancelExpand();
35037         return true;
35038     },
35039     
35040     afterRepair : function(data){
35041         if(data && Roo.enableFx){
35042             data.node.ui.highlight();
35043         }
35044         this.hideProxy();
35045     } 
35046     
35047 });
35048
35049 }
35050 /*
35051  * Based on:
35052  * Ext JS Library 1.1.1
35053  * Copyright(c) 2006-2007, Ext JS, LLC.
35054  *
35055  * Originally Released Under LGPL - original licence link has changed is not relivant.
35056  *
35057  * Fork - LGPL
35058  * <script type="text/javascript">
35059  */
35060  
35061
35062 if(Roo.dd.DragZone){
35063 Roo.tree.TreeDragZone = function(tree, config){
35064     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35065     this.tree = tree;
35066 };
35067
35068 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35069     ddGroup : "TreeDD",
35070    
35071     onBeforeDrag : function(data, e){
35072         var n = data.node;
35073         return n && n.draggable && !n.disabled;
35074     },
35075      
35076     
35077     onInitDrag : function(e){
35078         var data = this.dragData;
35079         this.tree.getSelectionModel().select(data.node);
35080         this.proxy.update("");
35081         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35082         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35083     },
35084     
35085     getRepairXY : function(e, data){
35086         return data.node.ui.getDDRepairXY();
35087     },
35088     
35089     onEndDrag : function(data, e){
35090         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35091         
35092         
35093     },
35094     
35095     onValidDrop : function(dd, e, id){
35096         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35097         this.hideProxy();
35098     },
35099     
35100     beforeInvalidDrop : function(e, id){
35101         // this scrolls the original position back into view
35102         var sm = this.tree.getSelectionModel();
35103         sm.clearSelections();
35104         sm.select(this.dragData.node);
35105     }
35106 });
35107 }/*
35108  * Based on:
35109  * Ext JS Library 1.1.1
35110  * Copyright(c) 2006-2007, Ext JS, LLC.
35111  *
35112  * Originally Released Under LGPL - original licence link has changed is not relivant.
35113  *
35114  * Fork - LGPL
35115  * <script type="text/javascript">
35116  */
35117 /**
35118  * @class Roo.tree.TreeEditor
35119  * @extends Roo.Editor
35120  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35121  * as the editor field.
35122  * @constructor
35123  * @param {Object} config (used to be the tree panel.)
35124  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35125  * 
35126  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35127  * @cfg {Roo.form.TextField|Object} field The field configuration
35128  *
35129  * 
35130  */
35131 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35132     var tree = config;
35133     var field;
35134     if (oldconfig) { // old style..
35135         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35136     } else {
35137         // new style..
35138         tree = config.tree;
35139         config.field = config.field  || {};
35140         config.field.xtype = 'TextField';
35141         field = Roo.factory(config.field, Roo.form);
35142     }
35143     config = config || {};
35144     
35145     
35146     this.addEvents({
35147         /**
35148          * @event beforenodeedit
35149          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35150          * false from the handler of this event.
35151          * @param {Editor} this
35152          * @param {Roo.tree.Node} node 
35153          */
35154         "beforenodeedit" : true
35155     });
35156     
35157     //Roo.log(config);
35158     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35159
35160     this.tree = tree;
35161
35162     tree.on('beforeclick', this.beforeNodeClick, this);
35163     tree.getTreeEl().on('mousedown', this.hide, this);
35164     this.on('complete', this.updateNode, this);
35165     this.on('beforestartedit', this.fitToTree, this);
35166     this.on('startedit', this.bindScroll, this, {delay:10});
35167     this.on('specialkey', this.onSpecialKey, this);
35168 };
35169
35170 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35171     /**
35172      * @cfg {String} alignment
35173      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35174      */
35175     alignment: "l-l",
35176     // inherit
35177     autoSize: false,
35178     /**
35179      * @cfg {Boolean} hideEl
35180      * True to hide the bound element while the editor is displayed (defaults to false)
35181      */
35182     hideEl : false,
35183     /**
35184      * @cfg {String} cls
35185      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35186      */
35187     cls: "x-small-editor x-tree-editor",
35188     /**
35189      * @cfg {Boolean} shim
35190      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35191      */
35192     shim:false,
35193     // inherit
35194     shadow:"frame",
35195     /**
35196      * @cfg {Number} maxWidth
35197      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35198      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35199      * scroll and client offsets into account prior to each edit.
35200      */
35201     maxWidth: 250,
35202
35203     editDelay : 350,
35204
35205     // private
35206     fitToTree : function(ed, el){
35207         var td = this.tree.getTreeEl().dom, nd = el.dom;
35208         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35209             td.scrollLeft = nd.offsetLeft;
35210         }
35211         var w = Math.min(
35212                 this.maxWidth,
35213                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35214         this.setSize(w, '');
35215         
35216         return this.fireEvent('beforenodeedit', this, this.editNode);
35217         
35218     },
35219
35220     // private
35221     triggerEdit : function(node){
35222         this.completeEdit();
35223         this.editNode = node;
35224         this.startEdit(node.ui.textNode, node.text);
35225     },
35226
35227     // private
35228     bindScroll : function(){
35229         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35230     },
35231
35232     // private
35233     beforeNodeClick : function(node, e){
35234         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35235         this.lastClick = new Date();
35236         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35237             e.stopEvent();
35238             this.triggerEdit(node);
35239             return false;
35240         }
35241         return true;
35242     },
35243
35244     // private
35245     updateNode : function(ed, value){
35246         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35247         this.editNode.setText(value);
35248     },
35249
35250     // private
35251     onHide : function(){
35252         Roo.tree.TreeEditor.superclass.onHide.call(this);
35253         if(this.editNode){
35254             this.editNode.ui.focus();
35255         }
35256     },
35257
35258     // private
35259     onSpecialKey : function(field, e){
35260         var k = e.getKey();
35261         if(k == e.ESC){
35262             e.stopEvent();
35263             this.cancelEdit();
35264         }else if(k == e.ENTER && !e.hasModifier()){
35265             e.stopEvent();
35266             this.completeEdit();
35267         }
35268     }
35269 });//<Script type="text/javascript">
35270 /*
35271  * Based on:
35272  * Ext JS Library 1.1.1
35273  * Copyright(c) 2006-2007, Ext JS, LLC.
35274  *
35275  * Originally Released Under LGPL - original licence link has changed is not relivant.
35276  *
35277  * Fork - LGPL
35278  * <script type="text/javascript">
35279  */
35280  
35281 /**
35282  * Not documented??? - probably should be...
35283  */
35284
35285 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35286     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35287     
35288     renderElements : function(n, a, targetNode, bulkRender){
35289         //consel.log("renderElements?");
35290         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35291
35292         var t = n.getOwnerTree();
35293         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35294         
35295         var cols = t.columns;
35296         var bw = t.borderWidth;
35297         var c = cols[0];
35298         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35299          var cb = typeof a.checked == "boolean";
35300         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35301         var colcls = 'x-t-' + tid + '-c0';
35302         var buf = [
35303             '<li class="x-tree-node">',
35304             
35305                 
35306                 '<div class="x-tree-node-el ', a.cls,'">',
35307                     // extran...
35308                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35309                 
35310                 
35311                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35312                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35313                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35314                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35315                            (a.iconCls ? ' '+a.iconCls : ''),
35316                            '" unselectable="on" />',
35317                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35318                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35319                              
35320                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35321                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35322                             '<span unselectable="on" qtip="' + tx + '">',
35323                              tx,
35324                              '</span></a>' ,
35325                     '</div>',
35326                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35327                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35328                  ];
35329         for(var i = 1, len = cols.length; i < len; i++){
35330             c = cols[i];
35331             colcls = 'x-t-' + tid + '-c' +i;
35332             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35333             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35334                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35335                       "</div>");
35336          }
35337          
35338          buf.push(
35339             '</a>',
35340             '<div class="x-clear"></div></div>',
35341             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35342             "</li>");
35343         
35344         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35345             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35346                                 n.nextSibling.ui.getEl(), buf.join(""));
35347         }else{
35348             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35349         }
35350         var el = this.wrap.firstChild;
35351         this.elRow = el;
35352         this.elNode = el.firstChild;
35353         this.ranchor = el.childNodes[1];
35354         this.ctNode = this.wrap.childNodes[1];
35355         var cs = el.firstChild.childNodes;
35356         this.indentNode = cs[0];
35357         this.ecNode = cs[1];
35358         this.iconNode = cs[2];
35359         var index = 3;
35360         if(cb){
35361             this.checkbox = cs[3];
35362             index++;
35363         }
35364         this.anchor = cs[index];
35365         
35366         this.textNode = cs[index].firstChild;
35367         
35368         //el.on("click", this.onClick, this);
35369         //el.on("dblclick", this.onDblClick, this);
35370         
35371         
35372        // console.log(this);
35373     },
35374     initEvents : function(){
35375         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35376         
35377             
35378         var a = this.ranchor;
35379
35380         var el = Roo.get(a);
35381
35382         if(Roo.isOpera){ // opera render bug ignores the CSS
35383             el.setStyle("text-decoration", "none");
35384         }
35385
35386         el.on("click", this.onClick, this);
35387         el.on("dblclick", this.onDblClick, this);
35388         el.on("contextmenu", this.onContextMenu, this);
35389         
35390     },
35391     
35392     /*onSelectedChange : function(state){
35393         if(state){
35394             this.focus();
35395             this.addClass("x-tree-selected");
35396         }else{
35397             //this.blur();
35398             this.removeClass("x-tree-selected");
35399         }
35400     },*/
35401     addClass : function(cls){
35402         if(this.elRow){
35403             Roo.fly(this.elRow).addClass(cls);
35404         }
35405         
35406     },
35407     
35408     
35409     removeClass : function(cls){
35410         if(this.elRow){
35411             Roo.fly(this.elRow).removeClass(cls);
35412         }
35413     }
35414
35415     
35416     
35417 });//<Script type="text/javascript">
35418
35419 /*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429  
35430
35431 /**
35432  * @class Roo.tree.ColumnTree
35433  * @extends Roo.data.TreePanel
35434  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35435  * @cfg {int} borderWidth  compined right/left border allowance
35436  * @constructor
35437  * @param {String/HTMLElement/Element} el The container element
35438  * @param {Object} config
35439  */
35440 Roo.tree.ColumnTree =  function(el, config)
35441 {
35442    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35443    this.addEvents({
35444         /**
35445         * @event resize
35446         * Fire this event on a container when it resizes
35447         * @param {int} w Width
35448         * @param {int} h Height
35449         */
35450        "resize" : true
35451     });
35452     this.on('resize', this.onResize, this);
35453 };
35454
35455 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35456     //lines:false,
35457     
35458     
35459     borderWidth: Roo.isBorderBox ? 0 : 2, 
35460     headEls : false,
35461     
35462     render : function(){
35463         // add the header.....
35464        
35465         Roo.tree.ColumnTree.superclass.render.apply(this);
35466         
35467         this.el.addClass('x-column-tree');
35468         
35469         this.headers = this.el.createChild(
35470             {cls:'x-tree-headers'},this.innerCt.dom);
35471    
35472         var cols = this.columns, c;
35473         var totalWidth = 0;
35474         this.headEls = [];
35475         var  len = cols.length;
35476         for(var i = 0; i < len; i++){
35477              c = cols[i];
35478              totalWidth += c.width;
35479             this.headEls.push(this.headers.createChild({
35480                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35481                  cn: {
35482                      cls:'x-tree-hd-text',
35483                      html: c.header
35484                  },
35485                  style:'width:'+(c.width-this.borderWidth)+'px;'
35486              }));
35487         }
35488         this.headers.createChild({cls:'x-clear'});
35489         // prevent floats from wrapping when clipped
35490         this.headers.setWidth(totalWidth);
35491         //this.innerCt.setWidth(totalWidth);
35492         this.innerCt.setStyle({ overflow: 'auto' });
35493         this.onResize(this.width, this.height);
35494              
35495         
35496     },
35497     onResize : function(w,h)
35498     {
35499         this.height = h;
35500         this.width = w;
35501         // resize cols..
35502         this.innerCt.setWidth(this.width);
35503         this.innerCt.setHeight(this.height-20);
35504         
35505         // headers...
35506         var cols = this.columns, c;
35507         var totalWidth = 0;
35508         var expEl = false;
35509         var len = cols.length;
35510         for(var i = 0; i < len; i++){
35511             c = cols[i];
35512             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35513                 // it's the expander..
35514                 expEl  = this.headEls[i];
35515                 continue;
35516             }
35517             totalWidth += c.width;
35518             
35519         }
35520         if (expEl) {
35521             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35522         }
35523         this.headers.setWidth(w-20);
35524
35525         
35526         
35527         
35528     }
35529 });
35530 /*
35531  * Based on:
35532  * Ext JS Library 1.1.1
35533  * Copyright(c) 2006-2007, Ext JS, LLC.
35534  *
35535  * Originally Released Under LGPL - original licence link has changed is not relivant.
35536  *
35537  * Fork - LGPL
35538  * <script type="text/javascript">
35539  */
35540  
35541 /**
35542  * @class Roo.menu.Menu
35543  * @extends Roo.util.Observable
35544  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35545  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35546  * @constructor
35547  * Creates a new Menu
35548  * @param {Object} config Configuration options
35549  */
35550 Roo.menu.Menu = function(config){
35551     Roo.apply(this, config);
35552     this.id = this.id || Roo.id();
35553     this.addEvents({
35554         /**
35555          * @event beforeshow
35556          * Fires before this menu is displayed
35557          * @param {Roo.menu.Menu} this
35558          */
35559         beforeshow : true,
35560         /**
35561          * @event beforehide
35562          * Fires before this menu is hidden
35563          * @param {Roo.menu.Menu} this
35564          */
35565         beforehide : true,
35566         /**
35567          * @event show
35568          * Fires after this menu is displayed
35569          * @param {Roo.menu.Menu} this
35570          */
35571         show : true,
35572         /**
35573          * @event hide
35574          * Fires after this menu is hidden
35575          * @param {Roo.menu.Menu} this
35576          */
35577         hide : true,
35578         /**
35579          * @event click
35580          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35581          * @param {Roo.menu.Menu} this
35582          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35583          * @param {Roo.EventObject} e
35584          */
35585         click : true,
35586         /**
35587          * @event mouseover
35588          * Fires when the mouse is hovering over this menu
35589          * @param {Roo.menu.Menu} this
35590          * @param {Roo.EventObject} e
35591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35592          */
35593         mouseover : true,
35594         /**
35595          * @event mouseout
35596          * Fires when the mouse exits this menu
35597          * @param {Roo.menu.Menu} this
35598          * @param {Roo.EventObject} e
35599          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35600          */
35601         mouseout : true,
35602         /**
35603          * @event itemclick
35604          * Fires when a menu item contained in this menu is clicked
35605          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35606          * @param {Roo.EventObject} e
35607          */
35608         itemclick: true
35609     });
35610     if (this.registerMenu) {
35611         Roo.menu.MenuMgr.register(this);
35612     }
35613     
35614     var mis = this.items;
35615     this.items = new Roo.util.MixedCollection();
35616     if(mis){
35617         this.add.apply(this, mis);
35618     }
35619 };
35620
35621 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35622     /**
35623      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35624      */
35625     minWidth : 120,
35626     /**
35627      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35628      * for bottom-right shadow (defaults to "sides")
35629      */
35630     shadow : "sides",
35631     /**
35632      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35633      * this menu (defaults to "tl-tr?")
35634      */
35635     subMenuAlign : "tl-tr?",
35636     /**
35637      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35638      * relative to its element of origin (defaults to "tl-bl?")
35639      */
35640     defaultAlign : "tl-bl?",
35641     /**
35642      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35643      */
35644     allowOtherMenus : false,
35645     /**
35646      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35647      */
35648     registerMenu : true,
35649
35650     hidden:true,
35651
35652     // private
35653     render : function(){
35654         if(this.el){
35655             return;
35656         }
35657         var el = this.el = new Roo.Layer({
35658             cls: "x-menu",
35659             shadow:this.shadow,
35660             constrain: false,
35661             parentEl: this.parentEl || document.body,
35662             zindex:15000
35663         });
35664
35665         this.keyNav = new Roo.menu.MenuNav(this);
35666
35667         if(this.plain){
35668             el.addClass("x-menu-plain");
35669         }
35670         if(this.cls){
35671             el.addClass(this.cls);
35672         }
35673         // generic focus element
35674         this.focusEl = el.createChild({
35675             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35676         });
35677         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35678         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35679         
35680         ul.on("mouseover", this.onMouseOver, this);
35681         ul.on("mouseout", this.onMouseOut, this);
35682         this.items.each(function(item){
35683             if (item.hidden) {
35684                 return;
35685             }
35686             
35687             var li = document.createElement("li");
35688             li.className = "x-menu-list-item";
35689             ul.dom.appendChild(li);
35690             item.render(li, this);
35691         }, this);
35692         this.ul = ul;
35693         this.autoWidth();
35694     },
35695
35696     // private
35697     autoWidth : function(){
35698         var el = this.el, ul = this.ul;
35699         if(!el){
35700             return;
35701         }
35702         var w = this.width;
35703         if(w){
35704             el.setWidth(w);
35705         }else if(Roo.isIE){
35706             el.setWidth(this.minWidth);
35707             var t = el.dom.offsetWidth; // force recalc
35708             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35709         }
35710     },
35711
35712     // private
35713     delayAutoWidth : function(){
35714         if(this.rendered){
35715             if(!this.awTask){
35716                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35717             }
35718             this.awTask.delay(20);
35719         }
35720     },
35721
35722     // private
35723     findTargetItem : function(e){
35724         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35725         if(t && t.menuItemId){
35726             return this.items.get(t.menuItemId);
35727         }
35728     },
35729
35730     // private
35731     onClick : function(e){
35732         Roo.log("menu.onClick");
35733         var t = this.findTargetItem(e);
35734         if(!t){
35735             return;
35736         }
35737         Roo.log(e);
35738         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35739             if(t == this.activeItem && t.shouldDeactivate(e)){
35740                 this.activeItem.deactivate();
35741                 delete this.activeItem;
35742                 return;
35743             }
35744             if(t.canActivate){
35745                 this.setActiveItem(t, true);
35746             }
35747             return;
35748             
35749             
35750         }
35751         
35752         t.onClick(e);
35753         this.fireEvent("click", this, t, e);
35754     },
35755
35756     // private
35757     setActiveItem : function(item, autoExpand){
35758         if(item != this.activeItem){
35759             if(this.activeItem){
35760                 this.activeItem.deactivate();
35761             }
35762             this.activeItem = item;
35763             item.activate(autoExpand);
35764         }else if(autoExpand){
35765             item.expandMenu();
35766         }
35767     },
35768
35769     // private
35770     tryActivate : function(start, step){
35771         var items = this.items;
35772         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35773             var item = items.get(i);
35774             if(!item.disabled && item.canActivate){
35775                 this.setActiveItem(item, false);
35776                 return item;
35777             }
35778         }
35779         return false;
35780     },
35781
35782     // private
35783     onMouseOver : function(e){
35784         var t;
35785         if(t = this.findTargetItem(e)){
35786             if(t.canActivate && !t.disabled){
35787                 this.setActiveItem(t, true);
35788             }
35789         }
35790         this.fireEvent("mouseover", this, e, t);
35791     },
35792
35793     // private
35794     onMouseOut : function(e){
35795         var t;
35796         if(t = this.findTargetItem(e)){
35797             if(t == this.activeItem && t.shouldDeactivate(e)){
35798                 this.activeItem.deactivate();
35799                 delete this.activeItem;
35800             }
35801         }
35802         this.fireEvent("mouseout", this, e, t);
35803     },
35804
35805     /**
35806      * Read-only.  Returns true if the menu is currently displayed, else false.
35807      * @type Boolean
35808      */
35809     isVisible : function(){
35810         return this.el && !this.hidden;
35811     },
35812
35813     /**
35814      * Displays this menu relative to another element
35815      * @param {String/HTMLElement/Roo.Element} element The element to align to
35816      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35817      * the element (defaults to this.defaultAlign)
35818      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35819      */
35820     show : function(el, pos, parentMenu){
35821         this.parentMenu = parentMenu;
35822         if(!this.el){
35823             this.render();
35824         }
35825         this.fireEvent("beforeshow", this);
35826         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35827     },
35828
35829     /**
35830      * Displays this menu at a specific xy position
35831      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35832      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35833      */
35834     showAt : function(xy, parentMenu, /* private: */_e){
35835         this.parentMenu = parentMenu;
35836         if(!this.el){
35837             this.render();
35838         }
35839         if(_e !== false){
35840             this.fireEvent("beforeshow", this);
35841             xy = this.el.adjustForConstraints(xy);
35842         }
35843         this.el.setXY(xy);
35844         this.el.show();
35845         this.hidden = false;
35846         this.focus();
35847         this.fireEvent("show", this);
35848     },
35849
35850     focus : function(){
35851         if(!this.hidden){
35852             this.doFocus.defer(50, this);
35853         }
35854     },
35855
35856     doFocus : function(){
35857         if(!this.hidden){
35858             this.focusEl.focus();
35859         }
35860     },
35861
35862     /**
35863      * Hides this menu and optionally all parent menus
35864      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35865      */
35866     hide : function(deep){
35867         if(this.el && this.isVisible()){
35868             this.fireEvent("beforehide", this);
35869             if(this.activeItem){
35870                 this.activeItem.deactivate();
35871                 this.activeItem = null;
35872             }
35873             this.el.hide();
35874             this.hidden = true;
35875             this.fireEvent("hide", this);
35876         }
35877         if(deep === true && this.parentMenu){
35878             this.parentMenu.hide(true);
35879         }
35880     },
35881
35882     /**
35883      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35884      * Any of the following are valid:
35885      * <ul>
35886      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35887      * <li>An HTMLElement object which will be converted to a menu item</li>
35888      * <li>A menu item config object that will be created as a new menu item</li>
35889      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35890      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35891      * </ul>
35892      * Usage:
35893      * <pre><code>
35894 // Create the menu
35895 var menu = new Roo.menu.Menu();
35896
35897 // Create a menu item to add by reference
35898 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35899
35900 // Add a bunch of items at once using different methods.
35901 // Only the last item added will be returned.
35902 var item = menu.add(
35903     menuItem,                // add existing item by ref
35904     'Dynamic Item',          // new TextItem
35905     '-',                     // new separator
35906     { text: 'Config Item' }  // new item by config
35907 );
35908 </code></pre>
35909      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35910      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35911      */
35912     add : function(){
35913         var a = arguments, l = a.length, item;
35914         for(var i = 0; i < l; i++){
35915             var el = a[i];
35916             if ((typeof(el) == "object") && el.xtype && el.xns) {
35917                 el = Roo.factory(el, Roo.menu);
35918             }
35919             
35920             if(el.render){ // some kind of Item
35921                 item = this.addItem(el);
35922             }else if(typeof el == "string"){ // string
35923                 if(el == "separator" || el == "-"){
35924                     item = this.addSeparator();
35925                 }else{
35926                     item = this.addText(el);
35927                 }
35928             }else if(el.tagName || el.el){ // element
35929                 item = this.addElement(el);
35930             }else if(typeof el == "object"){ // must be menu item config?
35931                 item = this.addMenuItem(el);
35932             }
35933         }
35934         return item;
35935     },
35936
35937     /**
35938      * Returns this menu's underlying {@link Roo.Element} object
35939      * @return {Roo.Element} The element
35940      */
35941     getEl : function(){
35942         if(!this.el){
35943             this.render();
35944         }
35945         return this.el;
35946     },
35947
35948     /**
35949      * Adds a separator bar to the menu
35950      * @return {Roo.menu.Item} The menu item that was added
35951      */
35952     addSeparator : function(){
35953         return this.addItem(new Roo.menu.Separator());
35954     },
35955
35956     /**
35957      * Adds an {@link Roo.Element} object to the menu
35958      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35959      * @return {Roo.menu.Item} The menu item that was added
35960      */
35961     addElement : function(el){
35962         return this.addItem(new Roo.menu.BaseItem(el));
35963     },
35964
35965     /**
35966      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35967      * @param {Roo.menu.Item} item The menu item to add
35968      * @return {Roo.menu.Item} The menu item that was added
35969      */
35970     addItem : function(item){
35971         this.items.add(item);
35972         if(this.ul){
35973             var li = document.createElement("li");
35974             li.className = "x-menu-list-item";
35975             this.ul.dom.appendChild(li);
35976             item.render(li, this);
35977             this.delayAutoWidth();
35978         }
35979         return item;
35980     },
35981
35982     /**
35983      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35984      * @param {Object} config A MenuItem config object
35985      * @return {Roo.menu.Item} The menu item that was added
35986      */
35987     addMenuItem : function(config){
35988         if(!(config instanceof Roo.menu.Item)){
35989             if(typeof config.checked == "boolean"){ // must be check menu item config?
35990                 config = new Roo.menu.CheckItem(config);
35991             }else{
35992                 config = new Roo.menu.Item(config);
35993             }
35994         }
35995         return this.addItem(config);
35996     },
35997
35998     /**
35999      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36000      * @param {String} text The text to display in the menu item
36001      * @return {Roo.menu.Item} The menu item that was added
36002      */
36003     addText : function(text){
36004         return this.addItem(new Roo.menu.TextItem({ text : text }));
36005     },
36006
36007     /**
36008      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36009      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36010      * @param {Roo.menu.Item} item The menu item to add
36011      * @return {Roo.menu.Item} The menu item that was added
36012      */
36013     insert : function(index, item){
36014         this.items.insert(index, item);
36015         if(this.ul){
36016             var li = document.createElement("li");
36017             li.className = "x-menu-list-item";
36018             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36019             item.render(li, this);
36020             this.delayAutoWidth();
36021         }
36022         return item;
36023     },
36024
36025     /**
36026      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36027      * @param {Roo.menu.Item} item The menu item to remove
36028      */
36029     remove : function(item){
36030         this.items.removeKey(item.id);
36031         item.destroy();
36032     },
36033
36034     /**
36035      * Removes and destroys all items in the menu
36036      */
36037     removeAll : function(){
36038         var f;
36039         while(f = this.items.first()){
36040             this.remove(f);
36041         }
36042     }
36043 });
36044
36045 // MenuNav is a private utility class used internally by the Menu
36046 Roo.menu.MenuNav = function(menu){
36047     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36048     this.scope = this.menu = menu;
36049 };
36050
36051 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36052     doRelay : function(e, h){
36053         var k = e.getKey();
36054         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36055             this.menu.tryActivate(0, 1);
36056             return false;
36057         }
36058         return h.call(this.scope || this, e, this.menu);
36059     },
36060
36061     up : function(e, m){
36062         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36063             m.tryActivate(m.items.length-1, -1);
36064         }
36065     },
36066
36067     down : function(e, m){
36068         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36069             m.tryActivate(0, 1);
36070         }
36071     },
36072
36073     right : function(e, m){
36074         if(m.activeItem){
36075             m.activeItem.expandMenu(true);
36076         }
36077     },
36078
36079     left : function(e, m){
36080         m.hide();
36081         if(m.parentMenu && m.parentMenu.activeItem){
36082             m.parentMenu.activeItem.activate();
36083         }
36084     },
36085
36086     enter : function(e, m){
36087         if(m.activeItem){
36088             e.stopPropagation();
36089             m.activeItem.onClick(e);
36090             m.fireEvent("click", this, m.activeItem);
36091             return true;
36092         }
36093     }
36094 });/*
36095  * Based on:
36096  * Ext JS Library 1.1.1
36097  * Copyright(c) 2006-2007, Ext JS, LLC.
36098  *
36099  * Originally Released Under LGPL - original licence link has changed is not relivant.
36100  *
36101  * Fork - LGPL
36102  * <script type="text/javascript">
36103  */
36104  
36105 /**
36106  * @class Roo.menu.MenuMgr
36107  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36108  * @singleton
36109  */
36110 Roo.menu.MenuMgr = function(){
36111    var menus, active, groups = {}, attached = false, lastShow = new Date();
36112
36113    // private - called when first menu is created
36114    function init(){
36115        menus = {};
36116        active = new Roo.util.MixedCollection();
36117        Roo.get(document).addKeyListener(27, function(){
36118            if(active.length > 0){
36119                hideAll();
36120            }
36121        });
36122    }
36123
36124    // private
36125    function hideAll(){
36126        if(active && active.length > 0){
36127            var c = active.clone();
36128            c.each(function(m){
36129                m.hide();
36130            });
36131        }
36132    }
36133
36134    // private
36135    function onHide(m){
36136        active.remove(m);
36137        if(active.length < 1){
36138            Roo.get(document).un("mousedown", onMouseDown);
36139            attached = false;
36140        }
36141    }
36142
36143    // private
36144    function onShow(m){
36145        var last = active.last();
36146        lastShow = new Date();
36147        active.add(m);
36148        if(!attached){
36149            Roo.get(document).on("mousedown", onMouseDown);
36150            attached = true;
36151        }
36152        if(m.parentMenu){
36153           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36154           m.parentMenu.activeChild = m;
36155        }else if(last && last.isVisible()){
36156           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36157        }
36158    }
36159
36160    // private
36161    function onBeforeHide(m){
36162        if(m.activeChild){
36163            m.activeChild.hide();
36164        }
36165        if(m.autoHideTimer){
36166            clearTimeout(m.autoHideTimer);
36167            delete m.autoHideTimer;
36168        }
36169    }
36170
36171    // private
36172    function onBeforeShow(m){
36173        var pm = m.parentMenu;
36174        if(!pm && !m.allowOtherMenus){
36175            hideAll();
36176        }else if(pm && pm.activeChild && active != m){
36177            pm.activeChild.hide();
36178        }
36179    }
36180
36181    // private
36182    function onMouseDown(e){
36183        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36184            hideAll();
36185        }
36186    }
36187
36188    // private
36189    function onBeforeCheck(mi, state){
36190        if(state){
36191            var g = groups[mi.group];
36192            for(var i = 0, l = g.length; i < l; i++){
36193                if(g[i] != mi){
36194                    g[i].setChecked(false);
36195                }
36196            }
36197        }
36198    }
36199
36200    return {
36201
36202        /**
36203         * Hides all menus that are currently visible
36204         */
36205        hideAll : function(){
36206             hideAll();  
36207        },
36208
36209        // private
36210        register : function(menu){
36211            if(!menus){
36212                init();
36213            }
36214            menus[menu.id] = menu;
36215            menu.on("beforehide", onBeforeHide);
36216            menu.on("hide", onHide);
36217            menu.on("beforeshow", onBeforeShow);
36218            menu.on("show", onShow);
36219            var g = menu.group;
36220            if(g && menu.events["checkchange"]){
36221                if(!groups[g]){
36222                    groups[g] = [];
36223                }
36224                groups[g].push(menu);
36225                menu.on("checkchange", onCheck);
36226            }
36227        },
36228
36229         /**
36230          * Returns a {@link Roo.menu.Menu} object
36231          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36232          * be used to generate and return a new Menu instance.
36233          */
36234        get : function(menu){
36235            if(typeof menu == "string"){ // menu id
36236                return menus[menu];
36237            }else if(menu.events){  // menu instance
36238                return menu;
36239            }else if(typeof menu.length == 'number'){ // array of menu items?
36240                return new Roo.menu.Menu({items:menu});
36241            }else{ // otherwise, must be a config
36242                return new Roo.menu.Menu(menu);
36243            }
36244        },
36245
36246        // private
36247        unregister : function(menu){
36248            delete menus[menu.id];
36249            menu.un("beforehide", onBeforeHide);
36250            menu.un("hide", onHide);
36251            menu.un("beforeshow", onBeforeShow);
36252            menu.un("show", onShow);
36253            var g = menu.group;
36254            if(g && menu.events["checkchange"]){
36255                groups[g].remove(menu);
36256                menu.un("checkchange", onCheck);
36257            }
36258        },
36259
36260        // private
36261        registerCheckable : function(menuItem){
36262            var g = menuItem.group;
36263            if(g){
36264                if(!groups[g]){
36265                    groups[g] = [];
36266                }
36267                groups[g].push(menuItem);
36268                menuItem.on("beforecheckchange", onBeforeCheck);
36269            }
36270        },
36271
36272        // private
36273        unregisterCheckable : function(menuItem){
36274            var g = menuItem.group;
36275            if(g){
36276                groups[g].remove(menuItem);
36277                menuItem.un("beforecheckchange", onBeforeCheck);
36278            }
36279        }
36280    };
36281 }();/*
36282  * Based on:
36283  * Ext JS Library 1.1.1
36284  * Copyright(c) 2006-2007, Ext JS, LLC.
36285  *
36286  * Originally Released Under LGPL - original licence link has changed is not relivant.
36287  *
36288  * Fork - LGPL
36289  * <script type="text/javascript">
36290  */
36291  
36292
36293 /**
36294  * @class Roo.menu.BaseItem
36295  * @extends Roo.Component
36296  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36297  * management and base configuration options shared by all menu components.
36298  * @constructor
36299  * Creates a new BaseItem
36300  * @param {Object} config Configuration options
36301  */
36302 Roo.menu.BaseItem = function(config){
36303     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36304
36305     this.addEvents({
36306         /**
36307          * @event click
36308          * Fires when this item is clicked
36309          * @param {Roo.menu.BaseItem} this
36310          * @param {Roo.EventObject} e
36311          */
36312         click: true,
36313         /**
36314          * @event activate
36315          * Fires when this item is activated
36316          * @param {Roo.menu.BaseItem} this
36317          */
36318         activate : true,
36319         /**
36320          * @event deactivate
36321          * Fires when this item is deactivated
36322          * @param {Roo.menu.BaseItem} this
36323          */
36324         deactivate : true
36325     });
36326
36327     if(this.handler){
36328         this.on("click", this.handler, this.scope, true);
36329     }
36330 };
36331
36332 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36333     /**
36334      * @cfg {Function} handler
36335      * A function that will handle the click event of this menu item (defaults to undefined)
36336      */
36337     /**
36338      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36339      */
36340     canActivate : false,
36341     
36342      /**
36343      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36344      */
36345     hidden: false,
36346     
36347     /**
36348      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36349      */
36350     activeClass : "x-menu-item-active",
36351     /**
36352      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36353      */
36354     hideOnClick : true,
36355     /**
36356      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36357      */
36358     hideDelay : 100,
36359
36360     // private
36361     ctype: "Roo.menu.BaseItem",
36362
36363     // private
36364     actionMode : "container",
36365
36366     // private
36367     render : function(container, parentMenu){
36368         this.parentMenu = parentMenu;
36369         Roo.menu.BaseItem.superclass.render.call(this, container);
36370         this.container.menuItemId = this.id;
36371     },
36372
36373     // private
36374     onRender : function(container, position){
36375         this.el = Roo.get(this.el);
36376         container.dom.appendChild(this.el.dom);
36377     },
36378
36379     // private
36380     onClick : function(e){
36381         if(!this.disabled && this.fireEvent("click", this, e) !== false
36382                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36383             this.handleClick(e);
36384         }else{
36385             e.stopEvent();
36386         }
36387     },
36388
36389     // private
36390     activate : function(){
36391         if(this.disabled){
36392             return false;
36393         }
36394         var li = this.container;
36395         li.addClass(this.activeClass);
36396         this.region = li.getRegion().adjust(2, 2, -2, -2);
36397         this.fireEvent("activate", this);
36398         return true;
36399     },
36400
36401     // private
36402     deactivate : function(){
36403         this.container.removeClass(this.activeClass);
36404         this.fireEvent("deactivate", this);
36405     },
36406
36407     // private
36408     shouldDeactivate : function(e){
36409         return !this.region || !this.region.contains(e.getPoint());
36410     },
36411
36412     // private
36413     handleClick : function(e){
36414         if(this.hideOnClick){
36415             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36416         }
36417     },
36418
36419     // private
36420     expandMenu : function(autoActivate){
36421         // do nothing
36422     },
36423
36424     // private
36425     hideMenu : function(){
36426         // do nothing
36427     }
36428 });/*
36429  * Based on:
36430  * Ext JS Library 1.1.1
36431  * Copyright(c) 2006-2007, Ext JS, LLC.
36432  *
36433  * Originally Released Under LGPL - original licence link has changed is not relivant.
36434  *
36435  * Fork - LGPL
36436  * <script type="text/javascript">
36437  */
36438  
36439 /**
36440  * @class Roo.menu.Adapter
36441  * @extends Roo.menu.BaseItem
36442  * 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.
36443  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36444  * @constructor
36445  * Creates a new Adapter
36446  * @param {Object} config Configuration options
36447  */
36448 Roo.menu.Adapter = function(component, config){
36449     Roo.menu.Adapter.superclass.constructor.call(this, config);
36450     this.component = component;
36451 };
36452 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36453     // private
36454     canActivate : true,
36455
36456     // private
36457     onRender : function(container, position){
36458         this.component.render(container);
36459         this.el = this.component.getEl();
36460     },
36461
36462     // private
36463     activate : function(){
36464         if(this.disabled){
36465             return false;
36466         }
36467         this.component.focus();
36468         this.fireEvent("activate", this);
36469         return true;
36470     },
36471
36472     // private
36473     deactivate : function(){
36474         this.fireEvent("deactivate", this);
36475     },
36476
36477     // private
36478     disable : function(){
36479         this.component.disable();
36480         Roo.menu.Adapter.superclass.disable.call(this);
36481     },
36482
36483     // private
36484     enable : function(){
36485         this.component.enable();
36486         Roo.menu.Adapter.superclass.enable.call(this);
36487     }
36488 });/*
36489  * Based on:
36490  * Ext JS Library 1.1.1
36491  * Copyright(c) 2006-2007, Ext JS, LLC.
36492  *
36493  * Originally Released Under LGPL - original licence link has changed is not relivant.
36494  *
36495  * Fork - LGPL
36496  * <script type="text/javascript">
36497  */
36498
36499 /**
36500  * @class Roo.menu.TextItem
36501  * @extends Roo.menu.BaseItem
36502  * Adds a static text string to a menu, usually used as either a heading or group separator.
36503  * Note: old style constructor with text is still supported.
36504  * 
36505  * @constructor
36506  * Creates a new TextItem
36507  * @param {Object} cfg Configuration
36508  */
36509 Roo.menu.TextItem = function(cfg){
36510     if (typeof(cfg) == 'string') {
36511         this.text = cfg;
36512     } else {
36513         Roo.apply(this,cfg);
36514     }
36515     
36516     Roo.menu.TextItem.superclass.constructor.call(this);
36517 };
36518
36519 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36520     /**
36521      * @cfg {Boolean} text Text to show on item.
36522      */
36523     text : '',
36524     
36525     /**
36526      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36527      */
36528     hideOnClick : false,
36529     /**
36530      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36531      */
36532     itemCls : "x-menu-text",
36533
36534     // private
36535     onRender : function(){
36536         var s = document.createElement("span");
36537         s.className = this.itemCls;
36538         s.innerHTML = this.text;
36539         this.el = s;
36540         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36541     }
36542 });/*
36543  * Based on:
36544  * Ext JS Library 1.1.1
36545  * Copyright(c) 2006-2007, Ext JS, LLC.
36546  *
36547  * Originally Released Under LGPL - original licence link has changed is not relivant.
36548  *
36549  * Fork - LGPL
36550  * <script type="text/javascript">
36551  */
36552
36553 /**
36554  * @class Roo.menu.Separator
36555  * @extends Roo.menu.BaseItem
36556  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36557  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36558  * @constructor
36559  * @param {Object} config Configuration options
36560  */
36561 Roo.menu.Separator = function(config){
36562     Roo.menu.Separator.superclass.constructor.call(this, config);
36563 };
36564
36565 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36566     /**
36567      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36568      */
36569     itemCls : "x-menu-sep",
36570     /**
36571      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36572      */
36573     hideOnClick : false,
36574
36575     // private
36576     onRender : function(li){
36577         var s = document.createElement("span");
36578         s.className = this.itemCls;
36579         s.innerHTML = "&#160;";
36580         this.el = s;
36581         li.addClass("x-menu-sep-li");
36582         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36583     }
36584 });/*
36585  * Based on:
36586  * Ext JS Library 1.1.1
36587  * Copyright(c) 2006-2007, Ext JS, LLC.
36588  *
36589  * Originally Released Under LGPL - original licence link has changed is not relivant.
36590  *
36591  * Fork - LGPL
36592  * <script type="text/javascript">
36593  */
36594 /**
36595  * @class Roo.menu.Item
36596  * @extends Roo.menu.BaseItem
36597  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36598  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36599  * activation and click handling.
36600  * @constructor
36601  * Creates a new Item
36602  * @param {Object} config Configuration options
36603  */
36604 Roo.menu.Item = function(config){
36605     Roo.menu.Item.superclass.constructor.call(this, config);
36606     if(this.menu){
36607         this.menu = Roo.menu.MenuMgr.get(this.menu);
36608     }
36609 };
36610 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36611     
36612     /**
36613      * @cfg {String} text
36614      * The text to show on the menu item.
36615      */
36616     text: '',
36617      /**
36618      * @cfg {String} HTML to render in menu
36619      * The text to show on the menu item (HTML version).
36620      */
36621     html: '',
36622     /**
36623      * @cfg {String} icon
36624      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36625      */
36626     icon: undefined,
36627     /**
36628      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36629      */
36630     itemCls : "x-menu-item",
36631     /**
36632      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36633      */
36634     canActivate : true,
36635     /**
36636      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36637      */
36638     showDelay: 200,
36639     // doc'd in BaseItem
36640     hideDelay: 200,
36641
36642     // private
36643     ctype: "Roo.menu.Item",
36644     
36645     // private
36646     onRender : function(container, position){
36647         var el = document.createElement("a");
36648         el.hideFocus = true;
36649         el.unselectable = "on";
36650         el.href = this.href || "#";
36651         if(this.hrefTarget){
36652             el.target = this.hrefTarget;
36653         }
36654         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36655         
36656         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36657         
36658         el.innerHTML = String.format(
36659                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36660                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36661         this.el = el;
36662         Roo.menu.Item.superclass.onRender.call(this, container, position);
36663     },
36664
36665     /**
36666      * Sets the text to display in this menu item
36667      * @param {String} text The text to display
36668      * @param {Boolean} isHTML true to indicate text is pure html.
36669      */
36670     setText : function(text, isHTML){
36671         if (isHTML) {
36672             this.html = text;
36673         } else {
36674             this.text = text;
36675             this.html = '';
36676         }
36677         if(this.rendered){
36678             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36679      
36680             this.el.update(String.format(
36681                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36682                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36683             this.parentMenu.autoWidth();
36684         }
36685     },
36686
36687     // private
36688     handleClick : function(e){
36689         if(!this.href){ // if no link defined, stop the event automatically
36690             e.stopEvent();
36691         }
36692         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36693     },
36694
36695     // private
36696     activate : function(autoExpand){
36697         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36698             this.focus();
36699             if(autoExpand){
36700                 this.expandMenu();
36701             }
36702         }
36703         return true;
36704     },
36705
36706     // private
36707     shouldDeactivate : function(e){
36708         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36709             if(this.menu && this.menu.isVisible()){
36710                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36711             }
36712             return true;
36713         }
36714         return false;
36715     },
36716
36717     // private
36718     deactivate : function(){
36719         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36720         this.hideMenu();
36721     },
36722
36723     // private
36724     expandMenu : function(autoActivate){
36725         if(!this.disabled && this.menu){
36726             clearTimeout(this.hideTimer);
36727             delete this.hideTimer;
36728             if(!this.menu.isVisible() && !this.showTimer){
36729                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36730             }else if (this.menu.isVisible() && autoActivate){
36731                 this.menu.tryActivate(0, 1);
36732             }
36733         }
36734     },
36735
36736     // private
36737     deferExpand : function(autoActivate){
36738         delete this.showTimer;
36739         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36740         if(autoActivate){
36741             this.menu.tryActivate(0, 1);
36742         }
36743     },
36744
36745     // private
36746     hideMenu : function(){
36747         clearTimeout(this.showTimer);
36748         delete this.showTimer;
36749         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36750             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36751         }
36752     },
36753
36754     // private
36755     deferHide : function(){
36756         delete this.hideTimer;
36757         this.menu.hide();
36758     }
36759 });/*
36760  * Based on:
36761  * Ext JS Library 1.1.1
36762  * Copyright(c) 2006-2007, Ext JS, LLC.
36763  *
36764  * Originally Released Under LGPL - original licence link has changed is not relivant.
36765  *
36766  * Fork - LGPL
36767  * <script type="text/javascript">
36768  */
36769  
36770 /**
36771  * @class Roo.menu.CheckItem
36772  * @extends Roo.menu.Item
36773  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36774  * @constructor
36775  * Creates a new CheckItem
36776  * @param {Object} config Configuration options
36777  */
36778 Roo.menu.CheckItem = function(config){
36779     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36780     this.addEvents({
36781         /**
36782          * @event beforecheckchange
36783          * Fires before the checked value is set, providing an opportunity to cancel if needed
36784          * @param {Roo.menu.CheckItem} this
36785          * @param {Boolean} checked The new checked value that will be set
36786          */
36787         "beforecheckchange" : true,
36788         /**
36789          * @event checkchange
36790          * Fires after the checked value has been set
36791          * @param {Roo.menu.CheckItem} this
36792          * @param {Boolean} checked The checked value that was set
36793          */
36794         "checkchange" : true
36795     });
36796     if(this.checkHandler){
36797         this.on('checkchange', this.checkHandler, this.scope);
36798     }
36799 };
36800 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36801     /**
36802      * @cfg {String} group
36803      * All check items with the same group name will automatically be grouped into a single-select
36804      * radio button group (defaults to '')
36805      */
36806     /**
36807      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36808      */
36809     itemCls : "x-menu-item x-menu-check-item",
36810     /**
36811      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36812      */
36813     groupClass : "x-menu-group-item",
36814
36815     /**
36816      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36817      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36818      * initialized with checked = true will be rendered as checked.
36819      */
36820     checked: false,
36821
36822     // private
36823     ctype: "Roo.menu.CheckItem",
36824
36825     // private
36826     onRender : function(c){
36827         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36828         if(this.group){
36829             this.el.addClass(this.groupClass);
36830         }
36831         Roo.menu.MenuMgr.registerCheckable(this);
36832         if(this.checked){
36833             this.checked = false;
36834             this.setChecked(true, true);
36835         }
36836     },
36837
36838     // private
36839     destroy : function(){
36840         if(this.rendered){
36841             Roo.menu.MenuMgr.unregisterCheckable(this);
36842         }
36843         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36844     },
36845
36846     /**
36847      * Set the checked state of this item
36848      * @param {Boolean} checked The new checked value
36849      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36850      */
36851     setChecked : function(state, suppressEvent){
36852         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36853             if(this.container){
36854                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36855             }
36856             this.checked = state;
36857             if(suppressEvent !== true){
36858                 this.fireEvent("checkchange", this, state);
36859             }
36860         }
36861     },
36862
36863     // private
36864     handleClick : function(e){
36865        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36866            this.setChecked(!this.checked);
36867        }
36868        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36869     }
36870 });/*
36871  * Based on:
36872  * Ext JS Library 1.1.1
36873  * Copyright(c) 2006-2007, Ext JS, LLC.
36874  *
36875  * Originally Released Under LGPL - original licence link has changed is not relivant.
36876  *
36877  * Fork - LGPL
36878  * <script type="text/javascript">
36879  */
36880  
36881 /**
36882  * @class Roo.menu.DateItem
36883  * @extends Roo.menu.Adapter
36884  * A menu item that wraps the {@link Roo.DatPicker} component.
36885  * @constructor
36886  * Creates a new DateItem
36887  * @param {Object} config Configuration options
36888  */
36889 Roo.menu.DateItem = function(config){
36890     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36891     /** The Roo.DatePicker object @type Roo.DatePicker */
36892     this.picker = this.component;
36893     this.addEvents({select: true});
36894     
36895     this.picker.on("render", function(picker){
36896         picker.getEl().swallowEvent("click");
36897         picker.container.addClass("x-menu-date-item");
36898     });
36899
36900     this.picker.on("select", this.onSelect, this);
36901 };
36902
36903 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36904     // private
36905     onSelect : function(picker, date){
36906         this.fireEvent("select", this, date, picker);
36907         Roo.menu.DateItem.superclass.handleClick.call(this);
36908     }
36909 });/*
36910  * Based on:
36911  * Ext JS Library 1.1.1
36912  * Copyright(c) 2006-2007, Ext JS, LLC.
36913  *
36914  * Originally Released Under LGPL - original licence link has changed is not relivant.
36915  *
36916  * Fork - LGPL
36917  * <script type="text/javascript">
36918  */
36919  
36920 /**
36921  * @class Roo.menu.ColorItem
36922  * @extends Roo.menu.Adapter
36923  * A menu item that wraps the {@link Roo.ColorPalette} component.
36924  * @constructor
36925  * Creates a new ColorItem
36926  * @param {Object} config Configuration options
36927  */
36928 Roo.menu.ColorItem = function(config){
36929     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36930     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36931     this.palette = this.component;
36932     this.relayEvents(this.palette, ["select"]);
36933     if(this.selectHandler){
36934         this.on('select', this.selectHandler, this.scope);
36935     }
36936 };
36937 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36938  * Based on:
36939  * Ext JS Library 1.1.1
36940  * Copyright(c) 2006-2007, Ext JS, LLC.
36941  *
36942  * Originally Released Under LGPL - original licence link has changed is not relivant.
36943  *
36944  * Fork - LGPL
36945  * <script type="text/javascript">
36946  */
36947  
36948
36949 /**
36950  * @class Roo.menu.DateMenu
36951  * @extends Roo.menu.Menu
36952  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36953  * @constructor
36954  * Creates a new DateMenu
36955  * @param {Object} config Configuration options
36956  */
36957 Roo.menu.DateMenu = function(config){
36958     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36959     this.plain = true;
36960     var di = new Roo.menu.DateItem(config);
36961     this.add(di);
36962     /**
36963      * The {@link Roo.DatePicker} instance for this DateMenu
36964      * @type DatePicker
36965      */
36966     this.picker = di.picker;
36967     /**
36968      * @event select
36969      * @param {DatePicker} picker
36970      * @param {Date} date
36971      */
36972     this.relayEvents(di, ["select"]);
36973     this.on('beforeshow', function(){
36974         if(this.picker){
36975             this.picker.hideMonthPicker(false);
36976         }
36977     }, this);
36978 };
36979 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36980     cls:'x-date-menu'
36981 });/*
36982  * Based on:
36983  * Ext JS Library 1.1.1
36984  * Copyright(c) 2006-2007, Ext JS, LLC.
36985  *
36986  * Originally Released Under LGPL - original licence link has changed is not relivant.
36987  *
36988  * Fork - LGPL
36989  * <script type="text/javascript">
36990  */
36991  
36992
36993 /**
36994  * @class Roo.menu.ColorMenu
36995  * @extends Roo.menu.Menu
36996  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36997  * @constructor
36998  * Creates a new ColorMenu
36999  * @param {Object} config Configuration options
37000  */
37001 Roo.menu.ColorMenu = function(config){
37002     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37003     this.plain = true;
37004     var ci = new Roo.menu.ColorItem(config);
37005     this.add(ci);
37006     /**
37007      * The {@link Roo.ColorPalette} instance for this ColorMenu
37008      * @type ColorPalette
37009      */
37010     this.palette = ci.palette;
37011     /**
37012      * @event select
37013      * @param {ColorPalette} palette
37014      * @param {String} color
37015      */
37016     this.relayEvents(ci, ["select"]);
37017 };
37018 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37019  * Based on:
37020  * Ext JS Library 1.1.1
37021  * Copyright(c) 2006-2007, Ext JS, LLC.
37022  *
37023  * Originally Released Under LGPL - original licence link has changed is not relivant.
37024  *
37025  * Fork - LGPL
37026  * <script type="text/javascript">
37027  */
37028  
37029 /**
37030  * @class Roo.form.Field
37031  * @extends Roo.BoxComponent
37032  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37033  * @constructor
37034  * Creates a new Field
37035  * @param {Object} config Configuration options
37036  */
37037 Roo.form.Field = function(config){
37038     Roo.form.Field.superclass.constructor.call(this, config);
37039 };
37040
37041 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37042     /**
37043      * @cfg {String} fieldLabel Label to use when rendering a form.
37044      */
37045        /**
37046      * @cfg {String} qtip Mouse over tip
37047      */
37048      
37049     /**
37050      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37051      */
37052     invalidClass : "x-form-invalid",
37053     /**
37054      * @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")
37055      */
37056     invalidText : "The value in this field is invalid",
37057     /**
37058      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37059      */
37060     focusClass : "x-form-focus",
37061     /**
37062      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37063       automatic validation (defaults to "keyup").
37064      */
37065     validationEvent : "keyup",
37066     /**
37067      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37068      */
37069     validateOnBlur : true,
37070     /**
37071      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37072      */
37073     validationDelay : 250,
37074     /**
37075      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37076      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37077      */
37078     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37079     /**
37080      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37081      */
37082     fieldClass : "x-form-field",
37083     /**
37084      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37085      *<pre>
37086 Value         Description
37087 -----------   ----------------------------------------------------------------------
37088 qtip          Display a quick tip when the user hovers over the field
37089 title         Display a default browser title attribute popup
37090 under         Add a block div beneath the field containing the error text
37091 side          Add an error icon to the right of the field with a popup on hover
37092 [element id]  Add the error text directly to the innerHTML of the specified element
37093 </pre>
37094      */
37095     msgTarget : 'qtip',
37096     /**
37097      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37098      */
37099     msgFx : 'normal',
37100
37101     /**
37102      * @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.
37103      */
37104     readOnly : false,
37105
37106     /**
37107      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37108      */
37109     disabled : false,
37110
37111     /**
37112      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37113      */
37114     inputType : undefined,
37115     
37116     /**
37117      * @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).
37118          */
37119         tabIndex : undefined,
37120         
37121     // private
37122     isFormField : true,
37123
37124     // private
37125     hasFocus : false,
37126     /**
37127      * @property {Roo.Element} fieldEl
37128      * Element Containing the rendered Field (with label etc.)
37129      */
37130     /**
37131      * @cfg {Mixed} value A value to initialize this field with.
37132      */
37133     value : undefined,
37134
37135     /**
37136      * @cfg {String} name The field's HTML name attribute.
37137      */
37138     /**
37139      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37140      */
37141
37142         // private ??
37143         initComponent : function(){
37144         Roo.form.Field.superclass.initComponent.call(this);
37145         this.addEvents({
37146             /**
37147              * @event focus
37148              * Fires when this field receives input focus.
37149              * @param {Roo.form.Field} this
37150              */
37151             focus : true,
37152             /**
37153              * @event blur
37154              * Fires when this field loses input focus.
37155              * @param {Roo.form.Field} this
37156              */
37157             blur : true,
37158             /**
37159              * @event specialkey
37160              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37161              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37162              * @param {Roo.form.Field} this
37163              * @param {Roo.EventObject} e The event object
37164              */
37165             specialkey : true,
37166             /**
37167              * @event change
37168              * Fires just before the field blurs if the field value has changed.
37169              * @param {Roo.form.Field} this
37170              * @param {Mixed} newValue The new value
37171              * @param {Mixed} oldValue The original value
37172              */
37173             change : true,
37174             /**
37175              * @event invalid
37176              * Fires after the field has been marked as invalid.
37177              * @param {Roo.form.Field} this
37178              * @param {String} msg The validation message
37179              */
37180             invalid : true,
37181             /**
37182              * @event valid
37183              * Fires after the field has been validated with no errors.
37184              * @param {Roo.form.Field} this
37185              */
37186             valid : true,
37187              /**
37188              * @event keyup
37189              * Fires after the key up
37190              * @param {Roo.form.Field} this
37191              * @param {Roo.EventObject}  e The event Object
37192              */
37193             keyup : true
37194         });
37195     },
37196
37197     /**
37198      * Returns the name attribute of the field if available
37199      * @return {String} name The field name
37200      */
37201     getName: function(){
37202          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37203     },
37204
37205     // private
37206     onRender : function(ct, position){
37207         Roo.form.Field.superclass.onRender.call(this, ct, position);
37208         if(!this.el){
37209             var cfg = this.getAutoCreate();
37210             if(!cfg.name){
37211                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37212             }
37213             if (!cfg.name.length) {
37214                 delete cfg.name;
37215             }
37216             if(this.inputType){
37217                 cfg.type = this.inputType;
37218             }
37219             this.el = ct.createChild(cfg, position);
37220         }
37221         var type = this.el.dom.type;
37222         if(type){
37223             if(type == 'password'){
37224                 type = 'text';
37225             }
37226             this.el.addClass('x-form-'+type);
37227         }
37228         if(this.readOnly){
37229             this.el.dom.readOnly = true;
37230         }
37231         if(this.tabIndex !== undefined){
37232             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37233         }
37234
37235         this.el.addClass([this.fieldClass, this.cls]);
37236         this.initValue();
37237     },
37238
37239     /**
37240      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37241      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37242      * @return {Roo.form.Field} this
37243      */
37244     applyTo : function(target){
37245         this.allowDomMove = false;
37246         this.el = Roo.get(target);
37247         this.render(this.el.dom.parentNode);
37248         return this;
37249     },
37250
37251     // private
37252     initValue : function(){
37253         if(this.value !== undefined){
37254             this.setValue(this.value);
37255         }else if(this.el.dom.value.length > 0){
37256             this.setValue(this.el.dom.value);
37257         }
37258     },
37259
37260     /**
37261      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37262      */
37263     isDirty : function() {
37264         if(this.disabled) {
37265             return false;
37266         }
37267         return String(this.getValue()) !== String(this.originalValue);
37268     },
37269
37270     // private
37271     afterRender : function(){
37272         Roo.form.Field.superclass.afterRender.call(this);
37273         this.initEvents();
37274     },
37275
37276     // private
37277     fireKey : function(e){
37278         //Roo.log('field ' + e.getKey());
37279         if(e.isNavKeyPress()){
37280             this.fireEvent("specialkey", this, e);
37281         }
37282     },
37283
37284     /**
37285      * Resets the current field value to the originally loaded value and clears any validation messages
37286      */
37287     reset : function(){
37288         this.setValue(this.resetValue);
37289         this.clearInvalid();
37290     },
37291
37292     // private
37293     initEvents : function(){
37294         // safari killled keypress - so keydown is now used..
37295         this.el.on("keydown" , this.fireKey,  this);
37296         this.el.on("focus", this.onFocus,  this);
37297         this.el.on("blur", this.onBlur,  this);
37298         this.el.relayEvent('keyup', this);
37299
37300         // reference to original value for reset
37301         this.originalValue = this.getValue();
37302         this.resetValue =  this.getValue();
37303     },
37304
37305     // private
37306     onFocus : function(){
37307         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37308             this.el.addClass(this.focusClass);
37309         }
37310         if(!this.hasFocus){
37311             this.hasFocus = true;
37312             this.startValue = this.getValue();
37313             this.fireEvent("focus", this);
37314         }
37315     },
37316
37317     beforeBlur : Roo.emptyFn,
37318
37319     // private
37320     onBlur : function(){
37321         this.beforeBlur();
37322         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37323             this.el.removeClass(this.focusClass);
37324         }
37325         this.hasFocus = false;
37326         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37327             this.validate();
37328         }
37329         var v = this.getValue();
37330         if(String(v) !== String(this.startValue)){
37331             this.fireEvent('change', this, v, this.startValue);
37332         }
37333         this.fireEvent("blur", this);
37334     },
37335
37336     /**
37337      * Returns whether or not the field value is currently valid
37338      * @param {Boolean} preventMark True to disable marking the field invalid
37339      * @return {Boolean} True if the value is valid, else false
37340      */
37341     isValid : function(preventMark){
37342         if(this.disabled){
37343             return true;
37344         }
37345         var restore = this.preventMark;
37346         this.preventMark = preventMark === true;
37347         var v = this.validateValue(this.processValue(this.getRawValue()));
37348         this.preventMark = restore;
37349         return v;
37350     },
37351
37352     /**
37353      * Validates the field value
37354      * @return {Boolean} True if the value is valid, else false
37355      */
37356     validate : function(){
37357         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37358             this.clearInvalid();
37359             return true;
37360         }
37361         return false;
37362     },
37363
37364     processValue : function(value){
37365         return value;
37366     },
37367
37368     // private
37369     // Subclasses should provide the validation implementation by overriding this
37370     validateValue : function(value){
37371         return true;
37372     },
37373
37374     /**
37375      * Mark this field as invalid
37376      * @param {String} msg The validation message
37377      */
37378     markInvalid : function(msg){
37379         if(!this.rendered || this.preventMark){ // not rendered
37380             return;
37381         }
37382         
37383         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37384         
37385         obj.el.addClass(this.invalidClass);
37386         msg = msg || this.invalidText;
37387         switch(this.msgTarget){
37388             case 'qtip':
37389                 obj.el.dom.qtip = msg;
37390                 obj.el.dom.qclass = 'x-form-invalid-tip';
37391                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37392                     Roo.QuickTips.enable();
37393                 }
37394                 break;
37395             case 'title':
37396                 this.el.dom.title = msg;
37397                 break;
37398             case 'under':
37399                 if(!this.errorEl){
37400                     var elp = this.el.findParent('.x-form-element', 5, true);
37401                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37402                     this.errorEl.setWidth(elp.getWidth(true)-20);
37403                 }
37404                 this.errorEl.update(msg);
37405                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37406                 break;
37407             case 'side':
37408                 if(!this.errorIcon){
37409                     var elp = this.el.findParent('.x-form-element', 5, true);
37410                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37411                 }
37412                 this.alignErrorIcon();
37413                 this.errorIcon.dom.qtip = msg;
37414                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37415                 this.errorIcon.show();
37416                 this.on('resize', this.alignErrorIcon, this);
37417                 break;
37418             default:
37419                 var t = Roo.getDom(this.msgTarget);
37420                 t.innerHTML = msg;
37421                 t.style.display = this.msgDisplay;
37422                 break;
37423         }
37424         this.fireEvent('invalid', this, msg);
37425     },
37426
37427     // private
37428     alignErrorIcon : function(){
37429         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37430     },
37431
37432     /**
37433      * Clear any invalid styles/messages for this field
37434      */
37435     clearInvalid : function(){
37436         if(!this.rendered || this.preventMark){ // not rendered
37437             return;
37438         }
37439         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37440         
37441         obj.el.removeClass(this.invalidClass);
37442         switch(this.msgTarget){
37443             case 'qtip':
37444                 obj.el.dom.qtip = '';
37445                 break;
37446             case 'title':
37447                 this.el.dom.title = '';
37448                 break;
37449             case 'under':
37450                 if(this.errorEl){
37451                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37452                 }
37453                 break;
37454             case 'side':
37455                 if(this.errorIcon){
37456                     this.errorIcon.dom.qtip = '';
37457                     this.errorIcon.hide();
37458                     this.un('resize', this.alignErrorIcon, this);
37459                 }
37460                 break;
37461             default:
37462                 var t = Roo.getDom(this.msgTarget);
37463                 t.innerHTML = '';
37464                 t.style.display = 'none';
37465                 break;
37466         }
37467         this.fireEvent('valid', this);
37468     },
37469
37470     /**
37471      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37472      * @return {Mixed} value The field value
37473      */
37474     getRawValue : function(){
37475         var v = this.el.getValue();
37476         
37477         return v;
37478     },
37479
37480     /**
37481      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37482      * @return {Mixed} value The field value
37483      */
37484     getValue : function(){
37485         var v = this.el.getValue();
37486          
37487         return v;
37488     },
37489
37490     /**
37491      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37492      * @param {Mixed} value The value to set
37493      */
37494     setRawValue : function(v){
37495         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37496     },
37497
37498     /**
37499      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37500      * @param {Mixed} value The value to set
37501      */
37502     setValue : function(v){
37503         this.value = v;
37504         if(this.rendered){
37505             this.el.dom.value = (v === null || v === undefined ? '' : v);
37506              this.validate();
37507         }
37508     },
37509
37510     adjustSize : function(w, h){
37511         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37512         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37513         return s;
37514     },
37515
37516     adjustWidth : function(tag, w){
37517         tag = tag.toLowerCase();
37518         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37519             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37520                 if(tag == 'input'){
37521                     return w + 2;
37522                 }
37523                 if(tag == 'textarea'){
37524                     return w-2;
37525                 }
37526             }else if(Roo.isOpera){
37527                 if(tag == 'input'){
37528                     return w + 2;
37529                 }
37530                 if(tag == 'textarea'){
37531                     return w-2;
37532                 }
37533             }
37534         }
37535         return w;
37536     }
37537 });
37538
37539
37540 // anything other than normal should be considered experimental
37541 Roo.form.Field.msgFx = {
37542     normal : {
37543         show: function(msgEl, f){
37544             msgEl.setDisplayed('block');
37545         },
37546
37547         hide : function(msgEl, f){
37548             msgEl.setDisplayed(false).update('');
37549         }
37550     },
37551
37552     slide : {
37553         show: function(msgEl, f){
37554             msgEl.slideIn('t', {stopFx:true});
37555         },
37556
37557         hide : function(msgEl, f){
37558             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37559         }
37560     },
37561
37562     slideRight : {
37563         show: function(msgEl, f){
37564             msgEl.fixDisplay();
37565             msgEl.alignTo(f.el, 'tl-tr');
37566             msgEl.slideIn('l', {stopFx:true});
37567         },
37568
37569         hide : function(msgEl, f){
37570             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37571         }
37572     }
37573 };/*
37574  * Based on:
37575  * Ext JS Library 1.1.1
37576  * Copyright(c) 2006-2007, Ext JS, LLC.
37577  *
37578  * Originally Released Under LGPL - original licence link has changed is not relivant.
37579  *
37580  * Fork - LGPL
37581  * <script type="text/javascript">
37582  */
37583  
37584
37585 /**
37586  * @class Roo.form.TextField
37587  * @extends Roo.form.Field
37588  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37589  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37590  * @constructor
37591  * Creates a new TextField
37592  * @param {Object} config Configuration options
37593  */
37594 Roo.form.TextField = function(config){
37595     Roo.form.TextField.superclass.constructor.call(this, config);
37596     this.addEvents({
37597         /**
37598          * @event autosize
37599          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37600          * according to the default logic, but this event provides a hook for the developer to apply additional
37601          * logic at runtime to resize the field if needed.
37602              * @param {Roo.form.Field} this This text field
37603              * @param {Number} width The new field width
37604              */
37605         autosize : true
37606     });
37607 };
37608
37609 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37610     /**
37611      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37612      */
37613     grow : false,
37614     /**
37615      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37616      */
37617     growMin : 30,
37618     /**
37619      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37620      */
37621     growMax : 800,
37622     /**
37623      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37624      */
37625     vtype : null,
37626     /**
37627      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37628      */
37629     maskRe : null,
37630     /**
37631      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37632      */
37633     disableKeyFilter : false,
37634     /**
37635      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37636      */
37637     allowBlank : true,
37638     /**
37639      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37640      */
37641     minLength : 0,
37642     /**
37643      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37644      */
37645     maxLength : Number.MAX_VALUE,
37646     /**
37647      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37648      */
37649     minLengthText : "The minimum length for this field is {0}",
37650     /**
37651      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37652      */
37653     maxLengthText : "The maximum length for this field is {0}",
37654     /**
37655      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37656      */
37657     selectOnFocus : false,
37658     /**
37659      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37660      */
37661     blankText : "This field is required",
37662     /**
37663      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37664      * If available, this function will be called only after the basic validators all return true, and will be passed the
37665      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37666      */
37667     validator : null,
37668     /**
37669      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37670      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37671      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37672      */
37673     regex : null,
37674     /**
37675      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37676      */
37677     regexText : "",
37678     /**
37679      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37680      */
37681     emptyText : null,
37682    
37683
37684     // private
37685     initEvents : function()
37686     {
37687         if (this.emptyText) {
37688             this.el.attr('placeholder', this.emptyText);
37689         }
37690         
37691         Roo.form.TextField.superclass.initEvents.call(this);
37692         if(this.validationEvent == 'keyup'){
37693             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37694             this.el.on('keyup', this.filterValidation, this);
37695         }
37696         else if(this.validationEvent !== false){
37697             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37698         }
37699         
37700         if(this.selectOnFocus){
37701             this.on("focus", this.preFocus, this);
37702             
37703         }
37704         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37705             this.el.on("keypress", this.filterKeys, this);
37706         }
37707         if(this.grow){
37708             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37709             this.el.on("click", this.autoSize,  this);
37710         }
37711         if(this.el.is('input[type=password]') && Roo.isSafari){
37712             this.el.on('keydown', this.SafariOnKeyDown, this);
37713         }
37714     },
37715
37716     processValue : function(value){
37717         if(this.stripCharsRe){
37718             var newValue = value.replace(this.stripCharsRe, '');
37719             if(newValue !== value){
37720                 this.setRawValue(newValue);
37721                 return newValue;
37722             }
37723         }
37724         return value;
37725     },
37726
37727     filterValidation : function(e){
37728         if(!e.isNavKeyPress()){
37729             this.validationTask.delay(this.validationDelay);
37730         }
37731     },
37732
37733     // private
37734     onKeyUp : function(e){
37735         if(!e.isNavKeyPress()){
37736             this.autoSize();
37737         }
37738     },
37739
37740     /**
37741      * Resets the current field value to the originally-loaded value and clears any validation messages.
37742      *  
37743      */
37744     reset : function(){
37745         Roo.form.TextField.superclass.reset.call(this);
37746        
37747     },
37748
37749     
37750     // private
37751     preFocus : function(){
37752         
37753         if(this.selectOnFocus){
37754             this.el.dom.select();
37755         }
37756     },
37757
37758     
37759     // private
37760     filterKeys : function(e){
37761         var k = e.getKey();
37762         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37763             return;
37764         }
37765         var c = e.getCharCode(), cc = String.fromCharCode(c);
37766         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37767             return;
37768         }
37769         if(!this.maskRe.test(cc)){
37770             e.stopEvent();
37771         }
37772     },
37773
37774     setValue : function(v){
37775         
37776         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37777         
37778         this.autoSize();
37779     },
37780
37781     /**
37782      * Validates a value according to the field's validation rules and marks the field as invalid
37783      * if the validation fails
37784      * @param {Mixed} value The value to validate
37785      * @return {Boolean} True if the value is valid, else false
37786      */
37787     validateValue : function(value){
37788         if(value.length < 1)  { // if it's blank
37789              if(this.allowBlank){
37790                 this.clearInvalid();
37791                 return true;
37792              }else{
37793                 this.markInvalid(this.blankText);
37794                 return false;
37795              }
37796         }
37797         if(value.length < this.minLength){
37798             this.markInvalid(String.format(this.minLengthText, this.minLength));
37799             return false;
37800         }
37801         if(value.length > this.maxLength){
37802             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37803             return false;
37804         }
37805         if(this.vtype){
37806             var vt = Roo.form.VTypes;
37807             if(!vt[this.vtype](value, this)){
37808                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37809                 return false;
37810             }
37811         }
37812         if(typeof this.validator == "function"){
37813             var msg = this.validator(value);
37814             if(msg !== true){
37815                 this.markInvalid(msg);
37816                 return false;
37817             }
37818         }
37819         if(this.regex && !this.regex.test(value)){
37820             this.markInvalid(this.regexText);
37821             return false;
37822         }
37823         return true;
37824     },
37825
37826     /**
37827      * Selects text in this field
37828      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37829      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37830      */
37831     selectText : function(start, end){
37832         var v = this.getRawValue();
37833         if(v.length > 0){
37834             start = start === undefined ? 0 : start;
37835             end = end === undefined ? v.length : end;
37836             var d = this.el.dom;
37837             if(d.setSelectionRange){
37838                 d.setSelectionRange(start, end);
37839             }else if(d.createTextRange){
37840                 var range = d.createTextRange();
37841                 range.moveStart("character", start);
37842                 range.moveEnd("character", v.length-end);
37843                 range.select();
37844             }
37845         }
37846     },
37847
37848     /**
37849      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37850      * This only takes effect if grow = true, and fires the autosize event.
37851      */
37852     autoSize : function(){
37853         if(!this.grow || !this.rendered){
37854             return;
37855         }
37856         if(!this.metrics){
37857             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37858         }
37859         var el = this.el;
37860         var v = el.dom.value;
37861         var d = document.createElement('div');
37862         d.appendChild(document.createTextNode(v));
37863         v = d.innerHTML;
37864         d = null;
37865         v += "&#160;";
37866         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37867         this.el.setWidth(w);
37868         this.fireEvent("autosize", this, w);
37869     },
37870     
37871     // private
37872     SafariOnKeyDown : function(event)
37873     {
37874         // this is a workaround for a password hang bug on chrome/ webkit.
37875         
37876         var isSelectAll = false;
37877         
37878         if(this.el.dom.selectionEnd > 0){
37879             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37880         }
37881         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37882             event.preventDefault();
37883             this.setValue('');
37884             return;
37885         }
37886         
37887         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37888             
37889             event.preventDefault();
37890             // this is very hacky as keydown always get's upper case.
37891             
37892             var cc = String.fromCharCode(event.getCharCode());
37893             
37894             
37895             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37896             
37897         }
37898         
37899         
37900     }
37901 });/*
37902  * Based on:
37903  * Ext JS Library 1.1.1
37904  * Copyright(c) 2006-2007, Ext JS, LLC.
37905  *
37906  * Originally Released Under LGPL - original licence link has changed is not relivant.
37907  *
37908  * Fork - LGPL
37909  * <script type="text/javascript">
37910  */
37911  
37912 /**
37913  * @class Roo.form.Hidden
37914  * @extends Roo.form.TextField
37915  * Simple Hidden element used on forms 
37916  * 
37917  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37918  * 
37919  * @constructor
37920  * Creates a new Hidden form element.
37921  * @param {Object} config Configuration options
37922  */
37923
37924
37925
37926 // easy hidden field...
37927 Roo.form.Hidden = function(config){
37928     Roo.form.Hidden.superclass.constructor.call(this, config);
37929 };
37930   
37931 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37932     fieldLabel:      '',
37933     inputType:      'hidden',
37934     width:          50,
37935     allowBlank:     true,
37936     labelSeparator: '',
37937     hidden:         true,
37938     itemCls :       'x-form-item-display-none'
37939
37940
37941 });
37942
37943
37944 /*
37945  * Based on:
37946  * Ext JS Library 1.1.1
37947  * Copyright(c) 2006-2007, Ext JS, LLC.
37948  *
37949  * Originally Released Under LGPL - original licence link has changed is not relivant.
37950  *
37951  * Fork - LGPL
37952  * <script type="text/javascript">
37953  */
37954  
37955 /**
37956  * @class Roo.form.TriggerField
37957  * @extends Roo.form.TextField
37958  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37959  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37960  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37961  * for which you can provide a custom implementation.  For example:
37962  * <pre><code>
37963 var trigger = new Roo.form.TriggerField();
37964 trigger.onTriggerClick = myTriggerFn;
37965 trigger.applyTo('my-field');
37966 </code></pre>
37967  *
37968  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37969  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37970  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37971  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37972  * @constructor
37973  * Create a new TriggerField.
37974  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37975  * to the base TextField)
37976  */
37977 Roo.form.TriggerField = function(config){
37978     this.mimicing = false;
37979     Roo.form.TriggerField.superclass.constructor.call(this, config);
37980 };
37981
37982 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37983     /**
37984      * @cfg {String} triggerClass A CSS class to apply to the trigger
37985      */
37986     /**
37987      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37988      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37989      */
37990     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
37991     /**
37992      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37993      */
37994     hideTrigger:false,
37995
37996     /** @cfg {Boolean} grow @hide */
37997     /** @cfg {Number} growMin @hide */
37998     /** @cfg {Number} growMax @hide */
37999
38000     /**
38001      * @hide 
38002      * @method
38003      */
38004     autoSize: Roo.emptyFn,
38005     // private
38006     monitorTab : true,
38007     // private
38008     deferHeight : true,
38009
38010     
38011     actionMode : 'wrap',
38012     // private
38013     onResize : function(w, h){
38014         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38015         if(typeof w == 'number'){
38016             var x = w - this.trigger.getWidth();
38017             this.el.setWidth(this.adjustWidth('input', x));
38018             this.trigger.setStyle('left', x+'px');
38019         }
38020     },
38021
38022     // private
38023     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38024
38025     // private
38026     getResizeEl : function(){
38027         return this.wrap;
38028     },
38029
38030     // private
38031     getPositionEl : function(){
38032         return this.wrap;
38033     },
38034
38035     // private
38036     alignErrorIcon : function(){
38037         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38038     },
38039
38040     // private
38041     onRender : function(ct, position){
38042         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38043         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38044         this.trigger = this.wrap.createChild(this.triggerConfig ||
38045                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38046         if(this.hideTrigger){
38047             this.trigger.setDisplayed(false);
38048         }
38049         this.initTrigger();
38050         if(!this.width){
38051             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38052         }
38053     },
38054
38055     // private
38056     initTrigger : function(){
38057         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38058         this.trigger.addClassOnOver('x-form-trigger-over');
38059         this.trigger.addClassOnClick('x-form-trigger-click');
38060     },
38061
38062     // private
38063     onDestroy : function(){
38064         if(this.trigger){
38065             this.trigger.removeAllListeners();
38066             this.trigger.remove();
38067         }
38068         if(this.wrap){
38069             this.wrap.remove();
38070         }
38071         Roo.form.TriggerField.superclass.onDestroy.call(this);
38072     },
38073
38074     // private
38075     onFocus : function(){
38076         Roo.form.TriggerField.superclass.onFocus.call(this);
38077         if(!this.mimicing){
38078             this.wrap.addClass('x-trigger-wrap-focus');
38079             this.mimicing = true;
38080             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38081             if(this.monitorTab){
38082                 this.el.on("keydown", this.checkTab, this);
38083             }
38084         }
38085     },
38086
38087     // private
38088     checkTab : function(e){
38089         if(e.getKey() == e.TAB){
38090             this.triggerBlur();
38091         }
38092     },
38093
38094     // private
38095     onBlur : function(){
38096         // do nothing
38097     },
38098
38099     // private
38100     mimicBlur : function(e, t){
38101         if(!this.wrap.contains(t) && this.validateBlur()){
38102             this.triggerBlur();
38103         }
38104     },
38105
38106     // private
38107     triggerBlur : function(){
38108         this.mimicing = false;
38109         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38110         if(this.monitorTab){
38111             this.el.un("keydown", this.checkTab, this);
38112         }
38113         this.wrap.removeClass('x-trigger-wrap-focus');
38114         Roo.form.TriggerField.superclass.onBlur.call(this);
38115     },
38116
38117     // private
38118     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38119     validateBlur : function(e, t){
38120         return true;
38121     },
38122
38123     // private
38124     onDisable : function(){
38125         Roo.form.TriggerField.superclass.onDisable.call(this);
38126         if(this.wrap){
38127             this.wrap.addClass('x-item-disabled');
38128         }
38129     },
38130
38131     // private
38132     onEnable : function(){
38133         Roo.form.TriggerField.superclass.onEnable.call(this);
38134         if(this.wrap){
38135             this.wrap.removeClass('x-item-disabled');
38136         }
38137     },
38138
38139     // private
38140     onShow : function(){
38141         var ae = this.getActionEl();
38142         
38143         if(ae){
38144             ae.dom.style.display = '';
38145             ae.dom.style.visibility = 'visible';
38146         }
38147     },
38148
38149     // private
38150     
38151     onHide : function(){
38152         var ae = this.getActionEl();
38153         ae.dom.style.display = 'none';
38154     },
38155
38156     /**
38157      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38158      * by an implementing function.
38159      * @method
38160      * @param {EventObject} e
38161      */
38162     onTriggerClick : Roo.emptyFn
38163 });
38164
38165 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38166 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38167 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38168 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38169     initComponent : function(){
38170         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38171
38172         this.triggerConfig = {
38173             tag:'span', cls:'x-form-twin-triggers', cn:[
38174             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38175             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38176         ]};
38177     },
38178
38179     getTrigger : function(index){
38180         return this.triggers[index];
38181     },
38182
38183     initTrigger : function(){
38184         var ts = this.trigger.select('.x-form-trigger', true);
38185         this.wrap.setStyle('overflow', 'hidden');
38186         var triggerField = this;
38187         ts.each(function(t, all, index){
38188             t.hide = function(){
38189                 var w = triggerField.wrap.getWidth();
38190                 this.dom.style.display = 'none';
38191                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38192             };
38193             t.show = function(){
38194                 var w = triggerField.wrap.getWidth();
38195                 this.dom.style.display = '';
38196                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38197             };
38198             var triggerIndex = 'Trigger'+(index+1);
38199
38200             if(this['hide'+triggerIndex]){
38201                 t.dom.style.display = 'none';
38202             }
38203             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38204             t.addClassOnOver('x-form-trigger-over');
38205             t.addClassOnClick('x-form-trigger-click');
38206         }, this);
38207         this.triggers = ts.elements;
38208     },
38209
38210     onTrigger1Click : Roo.emptyFn,
38211     onTrigger2Click : Roo.emptyFn
38212 });/*
38213  * Based on:
38214  * Ext JS Library 1.1.1
38215  * Copyright(c) 2006-2007, Ext JS, LLC.
38216  *
38217  * Originally Released Under LGPL - original licence link has changed is not relivant.
38218  *
38219  * Fork - LGPL
38220  * <script type="text/javascript">
38221  */
38222  
38223 /**
38224  * @class Roo.form.TextArea
38225  * @extends Roo.form.TextField
38226  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38227  * support for auto-sizing.
38228  * @constructor
38229  * Creates a new TextArea
38230  * @param {Object} config Configuration options
38231  */
38232 Roo.form.TextArea = function(config){
38233     Roo.form.TextArea.superclass.constructor.call(this, config);
38234     // these are provided exchanges for backwards compat
38235     // minHeight/maxHeight were replaced by growMin/growMax to be
38236     // compatible with TextField growing config values
38237     if(this.minHeight !== undefined){
38238         this.growMin = this.minHeight;
38239     }
38240     if(this.maxHeight !== undefined){
38241         this.growMax = this.maxHeight;
38242     }
38243 };
38244
38245 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38246     /**
38247      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38248      */
38249     growMin : 60,
38250     /**
38251      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38252      */
38253     growMax: 1000,
38254     /**
38255      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38256      * in the field (equivalent to setting overflow: hidden, defaults to false)
38257      */
38258     preventScrollbars: false,
38259     /**
38260      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38261      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38262      */
38263
38264     // private
38265     onRender : function(ct, position){
38266         if(!this.el){
38267             this.defaultAutoCreate = {
38268                 tag: "textarea",
38269                 style:"width:300px;height:60px;",
38270                 autocomplete: "new-password"
38271             };
38272         }
38273         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38274         if(this.grow){
38275             this.textSizeEl = Roo.DomHelper.append(document.body, {
38276                 tag: "pre", cls: "x-form-grow-sizer"
38277             });
38278             if(this.preventScrollbars){
38279                 this.el.setStyle("overflow", "hidden");
38280             }
38281             this.el.setHeight(this.growMin);
38282         }
38283     },
38284
38285     onDestroy : function(){
38286         if(this.textSizeEl){
38287             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38288         }
38289         Roo.form.TextArea.superclass.onDestroy.call(this);
38290     },
38291
38292     // private
38293     onKeyUp : function(e){
38294         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38295             this.autoSize();
38296         }
38297     },
38298
38299     /**
38300      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38301      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38302      */
38303     autoSize : function(){
38304         if(!this.grow || !this.textSizeEl){
38305             return;
38306         }
38307         var el = this.el;
38308         var v = el.dom.value;
38309         var ts = this.textSizeEl;
38310
38311         ts.innerHTML = '';
38312         ts.appendChild(document.createTextNode(v));
38313         v = ts.innerHTML;
38314
38315         Roo.fly(ts).setWidth(this.el.getWidth());
38316         if(v.length < 1){
38317             v = "&#160;&#160;";
38318         }else{
38319             if(Roo.isIE){
38320                 v = v.replace(/\n/g, '<p>&#160;</p>');
38321             }
38322             v += "&#160;\n&#160;";
38323         }
38324         ts.innerHTML = v;
38325         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38326         if(h != this.lastHeight){
38327             this.lastHeight = h;
38328             this.el.setHeight(h);
38329             this.fireEvent("autosize", this, h);
38330         }
38331     }
38332 });/*
38333  * Based on:
38334  * Ext JS Library 1.1.1
38335  * Copyright(c) 2006-2007, Ext JS, LLC.
38336  *
38337  * Originally Released Under LGPL - original licence link has changed is not relivant.
38338  *
38339  * Fork - LGPL
38340  * <script type="text/javascript">
38341  */
38342  
38343
38344 /**
38345  * @class Roo.form.NumberField
38346  * @extends Roo.form.TextField
38347  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38348  * @constructor
38349  * Creates a new NumberField
38350  * @param {Object} config Configuration options
38351  */
38352 Roo.form.NumberField = function(config){
38353     Roo.form.NumberField.superclass.constructor.call(this, config);
38354 };
38355
38356 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38357     /**
38358      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38359      */
38360     fieldClass: "x-form-field x-form-num-field",
38361     /**
38362      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38363      */
38364     allowDecimals : true,
38365     /**
38366      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38367      */
38368     decimalSeparator : ".",
38369     /**
38370      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38371      */
38372     decimalPrecision : 2,
38373     /**
38374      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38375      */
38376     allowNegative : true,
38377     /**
38378      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38379      */
38380     minValue : Number.NEGATIVE_INFINITY,
38381     /**
38382      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38383      */
38384     maxValue : Number.MAX_VALUE,
38385     /**
38386      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38387      */
38388     minText : "The minimum value for this field is {0}",
38389     /**
38390      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38391      */
38392     maxText : "The maximum value for this field is {0}",
38393     /**
38394      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38395      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38396      */
38397     nanText : "{0} is not a valid number",
38398
38399     // private
38400     initEvents : function(){
38401         Roo.form.NumberField.superclass.initEvents.call(this);
38402         var allowed = "0123456789";
38403         if(this.allowDecimals){
38404             allowed += this.decimalSeparator;
38405         }
38406         if(this.allowNegative){
38407             allowed += "-";
38408         }
38409         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38410         var keyPress = function(e){
38411             var k = e.getKey();
38412             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38413                 return;
38414             }
38415             var c = e.getCharCode();
38416             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38417                 e.stopEvent();
38418             }
38419         };
38420         this.el.on("keypress", keyPress, this);
38421     },
38422
38423     // private
38424     validateValue : function(value){
38425         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38426             return false;
38427         }
38428         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38429              return true;
38430         }
38431         var num = this.parseValue(value);
38432         if(isNaN(num)){
38433             this.markInvalid(String.format(this.nanText, value));
38434             return false;
38435         }
38436         if(num < this.minValue){
38437             this.markInvalid(String.format(this.minText, this.minValue));
38438             return false;
38439         }
38440         if(num > this.maxValue){
38441             this.markInvalid(String.format(this.maxText, this.maxValue));
38442             return false;
38443         }
38444         return true;
38445     },
38446
38447     getValue : function(){
38448         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38449     },
38450
38451     // private
38452     parseValue : function(value){
38453         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38454         return isNaN(value) ? '' : value;
38455     },
38456
38457     // private
38458     fixPrecision : function(value){
38459         var nan = isNaN(value);
38460         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38461             return nan ? '' : value;
38462         }
38463         return parseFloat(value).toFixed(this.decimalPrecision);
38464     },
38465
38466     setValue : function(v){
38467         v = this.fixPrecision(v);
38468         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38469     },
38470
38471     // private
38472     decimalPrecisionFcn : function(v){
38473         return Math.floor(v);
38474     },
38475
38476     beforeBlur : function(){
38477         var v = this.parseValue(this.getRawValue());
38478         if(v){
38479             this.setValue(v);
38480         }
38481     }
38482 });/*
38483  * Based on:
38484  * Ext JS Library 1.1.1
38485  * Copyright(c) 2006-2007, Ext JS, LLC.
38486  *
38487  * Originally Released Under LGPL - original licence link has changed is not relivant.
38488  *
38489  * Fork - LGPL
38490  * <script type="text/javascript">
38491  */
38492  
38493 /**
38494  * @class Roo.form.DateField
38495  * @extends Roo.form.TriggerField
38496  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38497 * @constructor
38498 * Create a new DateField
38499 * @param {Object} config
38500  */
38501 Roo.form.DateField = function(config){
38502     Roo.form.DateField.superclass.constructor.call(this, config);
38503     
38504       this.addEvents({
38505          
38506         /**
38507          * @event select
38508          * Fires when a date is selected
38509              * @param {Roo.form.DateField} combo This combo box
38510              * @param {Date} date The date selected
38511              */
38512         'select' : true
38513          
38514     });
38515     
38516     
38517     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38518     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38519     this.ddMatch = null;
38520     if(this.disabledDates){
38521         var dd = this.disabledDates;
38522         var re = "(?:";
38523         for(var i = 0; i < dd.length; i++){
38524             re += dd[i];
38525             if(i != dd.length-1) re += "|";
38526         }
38527         this.ddMatch = new RegExp(re + ")");
38528     }
38529 };
38530
38531 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38532     /**
38533      * @cfg {String} format
38534      * The default date format string which can be overriden for localization support.  The format must be
38535      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38536      */
38537     format : "m/d/y",
38538     /**
38539      * @cfg {String} altFormats
38540      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38541      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38542      */
38543     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38544     /**
38545      * @cfg {Array} disabledDays
38546      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38547      */
38548     disabledDays : null,
38549     /**
38550      * @cfg {String} disabledDaysText
38551      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38552      */
38553     disabledDaysText : "Disabled",
38554     /**
38555      * @cfg {Array} disabledDates
38556      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38557      * expression so they are very powerful. Some examples:
38558      * <ul>
38559      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38560      * <li>["03/08", "09/16"] would disable those days for every year</li>
38561      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38562      * <li>["03/../2006"] would disable every day in March 2006</li>
38563      * <li>["^03"] would disable every day in every March</li>
38564      * </ul>
38565      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38566      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38567      */
38568     disabledDates : null,
38569     /**
38570      * @cfg {String} disabledDatesText
38571      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38572      */
38573     disabledDatesText : "Disabled",
38574     /**
38575      * @cfg {Date/String} minValue
38576      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38577      * valid format (defaults to null).
38578      */
38579     minValue : null,
38580     /**
38581      * @cfg {Date/String} maxValue
38582      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38583      * valid format (defaults to null).
38584      */
38585     maxValue : null,
38586     /**
38587      * @cfg {String} minText
38588      * The error text to display when the date in the cell is before minValue (defaults to
38589      * 'The date in this field must be after {minValue}').
38590      */
38591     minText : "The date in this field must be equal to or after {0}",
38592     /**
38593      * @cfg {String} maxText
38594      * The error text to display when the date in the cell is after maxValue (defaults to
38595      * 'The date in this field must be before {maxValue}').
38596      */
38597     maxText : "The date in this field must be equal to or before {0}",
38598     /**
38599      * @cfg {String} invalidText
38600      * The error text to display when the date in the field is invalid (defaults to
38601      * '{value} is not a valid date - it must be in the format {format}').
38602      */
38603     invalidText : "{0} is not a valid date - it must be in the format {1}",
38604     /**
38605      * @cfg {String} triggerClass
38606      * An additional CSS class used to style the trigger button.  The trigger will always get the
38607      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38608      * which displays a calendar icon).
38609      */
38610     triggerClass : 'x-form-date-trigger',
38611     
38612
38613     /**
38614      * @cfg {Boolean} useIso
38615      * if enabled, then the date field will use a hidden field to store the 
38616      * real value as iso formated date. default (false)
38617      */ 
38618     useIso : false,
38619     /**
38620      * @cfg {String/Object} autoCreate
38621      * A DomHelper element spec, or true for a default element spec (defaults to
38622      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38623      */ 
38624     // private
38625     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38626     
38627     // private
38628     hiddenField: false,
38629     
38630     onRender : function(ct, position)
38631     {
38632         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38633         if (this.useIso) {
38634             //this.el.dom.removeAttribute('name'); 
38635             Roo.log("Changing name?");
38636             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38637             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38638                     'before', true);
38639             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38640             // prevent input submission
38641             this.hiddenName = this.name;
38642         }
38643             
38644             
38645     },
38646     
38647     // private
38648     validateValue : function(value)
38649     {
38650         value = this.formatDate(value);
38651         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38652             Roo.log('super failed');
38653             return false;
38654         }
38655         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38656              return true;
38657         }
38658         var svalue = value;
38659         value = this.parseDate(value);
38660         if(!value){
38661             Roo.log('parse date failed' + svalue);
38662             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38663             return false;
38664         }
38665         var time = value.getTime();
38666         if(this.minValue && time < this.minValue.getTime()){
38667             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38668             return false;
38669         }
38670         if(this.maxValue && time > this.maxValue.getTime()){
38671             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38672             return false;
38673         }
38674         if(this.disabledDays){
38675             var day = value.getDay();
38676             for(var i = 0; i < this.disabledDays.length; i++) {
38677                 if(day === this.disabledDays[i]){
38678                     this.markInvalid(this.disabledDaysText);
38679                     return false;
38680                 }
38681             }
38682         }
38683         var fvalue = this.formatDate(value);
38684         if(this.ddMatch && this.ddMatch.test(fvalue)){
38685             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38686             return false;
38687         }
38688         return true;
38689     },
38690
38691     // private
38692     // Provides logic to override the default TriggerField.validateBlur which just returns true
38693     validateBlur : function(){
38694         return !this.menu || !this.menu.isVisible();
38695     },
38696     
38697     getName: function()
38698     {
38699         // returns hidden if it's set..
38700         if (!this.rendered) {return ''};
38701         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38702         
38703     },
38704
38705     /**
38706      * Returns the current date value of the date field.
38707      * @return {Date} The date value
38708      */
38709     getValue : function(){
38710         
38711         return  this.hiddenField ?
38712                 this.hiddenField.value :
38713                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38714     },
38715
38716     /**
38717      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38718      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38719      * (the default format used is "m/d/y").
38720      * <br />Usage:
38721      * <pre><code>
38722 //All of these calls set the same date value (May 4, 2006)
38723
38724 //Pass a date object:
38725 var dt = new Date('5/4/06');
38726 dateField.setValue(dt);
38727
38728 //Pass a date string (default format):
38729 dateField.setValue('5/4/06');
38730
38731 //Pass a date string (custom format):
38732 dateField.format = 'Y-m-d';
38733 dateField.setValue('2006-5-4');
38734 </code></pre>
38735      * @param {String/Date} date The date or valid date string
38736      */
38737     setValue : function(date){
38738         if (this.hiddenField) {
38739             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38740         }
38741         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38742         // make sure the value field is always stored as a date..
38743         this.value = this.parseDate(date);
38744         
38745         
38746     },
38747
38748     // private
38749     parseDate : function(value){
38750         if(!value || value instanceof Date){
38751             return value;
38752         }
38753         var v = Date.parseDate(value, this.format);
38754          if (!v && this.useIso) {
38755             v = Date.parseDate(value, 'Y-m-d');
38756         }
38757         if(!v && this.altFormats){
38758             if(!this.altFormatsArray){
38759                 this.altFormatsArray = this.altFormats.split("|");
38760             }
38761             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38762                 v = Date.parseDate(value, this.altFormatsArray[i]);
38763             }
38764         }
38765         return v;
38766     },
38767
38768     // private
38769     formatDate : function(date, fmt){
38770         return (!date || !(date instanceof Date)) ?
38771                date : date.dateFormat(fmt || this.format);
38772     },
38773
38774     // private
38775     menuListeners : {
38776         select: function(m, d){
38777             
38778             this.setValue(d);
38779             this.fireEvent('select', this, d);
38780         },
38781         show : function(){ // retain focus styling
38782             this.onFocus();
38783         },
38784         hide : function(){
38785             this.focus.defer(10, this);
38786             var ml = this.menuListeners;
38787             this.menu.un("select", ml.select,  this);
38788             this.menu.un("show", ml.show,  this);
38789             this.menu.un("hide", ml.hide,  this);
38790         }
38791     },
38792
38793     // private
38794     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38795     onTriggerClick : function(){
38796         if(this.disabled){
38797             return;
38798         }
38799         if(this.menu == null){
38800             this.menu = new Roo.menu.DateMenu();
38801         }
38802         Roo.apply(this.menu.picker,  {
38803             showClear: this.allowBlank,
38804             minDate : this.minValue,
38805             maxDate : this.maxValue,
38806             disabledDatesRE : this.ddMatch,
38807             disabledDatesText : this.disabledDatesText,
38808             disabledDays : this.disabledDays,
38809             disabledDaysText : this.disabledDaysText,
38810             format : this.useIso ? 'Y-m-d' : this.format,
38811             minText : String.format(this.minText, this.formatDate(this.minValue)),
38812             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38813         });
38814         this.menu.on(Roo.apply({}, this.menuListeners, {
38815             scope:this
38816         }));
38817         this.menu.picker.setValue(this.getValue() || new Date());
38818         this.menu.show(this.el, "tl-bl?");
38819     },
38820
38821     beforeBlur : function(){
38822         var v = this.parseDate(this.getRawValue());
38823         if(v){
38824             this.setValue(v);
38825         }
38826     },
38827
38828     /*@
38829      * overide
38830      * 
38831      */
38832     isDirty : function() {
38833         if(this.disabled) {
38834             return false;
38835         }
38836         
38837         if(typeof(this.startValue) === 'undefined'){
38838             return false;
38839         }
38840         
38841         return String(this.getValue()) !== String(this.startValue);
38842         
38843     }
38844 });/*
38845  * Based on:
38846  * Ext JS Library 1.1.1
38847  * Copyright(c) 2006-2007, Ext JS, LLC.
38848  *
38849  * Originally Released Under LGPL - original licence link has changed is not relivant.
38850  *
38851  * Fork - LGPL
38852  * <script type="text/javascript">
38853  */
38854  
38855 /**
38856  * @class Roo.form.MonthField
38857  * @extends Roo.form.TriggerField
38858  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38859 * @constructor
38860 * Create a new MonthField
38861 * @param {Object} config
38862  */
38863 Roo.form.MonthField = function(config){
38864     
38865     Roo.form.MonthField.superclass.constructor.call(this, config);
38866     
38867       this.addEvents({
38868          
38869         /**
38870          * @event select
38871          * Fires when a date is selected
38872              * @param {Roo.form.MonthFieeld} combo This combo box
38873              * @param {Date} date The date selected
38874              */
38875         'select' : true
38876          
38877     });
38878     
38879     
38880     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38881     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38882     this.ddMatch = null;
38883     if(this.disabledDates){
38884         var dd = this.disabledDates;
38885         var re = "(?:";
38886         for(var i = 0; i < dd.length; i++){
38887             re += dd[i];
38888             if(i != dd.length-1) re += "|";
38889         }
38890         this.ddMatch = new RegExp(re + ")");
38891     }
38892 };
38893
38894 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38895     /**
38896      * @cfg {String} format
38897      * The default date format string which can be overriden for localization support.  The format must be
38898      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38899      */
38900     format : "M Y",
38901     /**
38902      * @cfg {String} altFormats
38903      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38904      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38905      */
38906     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38907     /**
38908      * @cfg {Array} disabledDays
38909      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38910      */
38911     disabledDays : [0,1,2,3,4,5,6],
38912     /**
38913      * @cfg {String} disabledDaysText
38914      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38915      */
38916     disabledDaysText : "Disabled",
38917     /**
38918      * @cfg {Array} disabledDates
38919      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38920      * expression so they are very powerful. Some examples:
38921      * <ul>
38922      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38923      * <li>["03/08", "09/16"] would disable those days for every year</li>
38924      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38925      * <li>["03/../2006"] would disable every day in March 2006</li>
38926      * <li>["^03"] would disable every day in every March</li>
38927      * </ul>
38928      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38929      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38930      */
38931     disabledDates : null,
38932     /**
38933      * @cfg {String} disabledDatesText
38934      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38935      */
38936     disabledDatesText : "Disabled",
38937     /**
38938      * @cfg {Date/String} minValue
38939      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38940      * valid format (defaults to null).
38941      */
38942     minValue : null,
38943     /**
38944      * @cfg {Date/String} maxValue
38945      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38946      * valid format (defaults to null).
38947      */
38948     maxValue : null,
38949     /**
38950      * @cfg {String} minText
38951      * The error text to display when the date in the cell is before minValue (defaults to
38952      * 'The date in this field must be after {minValue}').
38953      */
38954     minText : "The date in this field must be equal to or after {0}",
38955     /**
38956      * @cfg {String} maxTextf
38957      * The error text to display when the date in the cell is after maxValue (defaults to
38958      * 'The date in this field must be before {maxValue}').
38959      */
38960     maxText : "The date in this field must be equal to or before {0}",
38961     /**
38962      * @cfg {String} invalidText
38963      * The error text to display when the date in the field is invalid (defaults to
38964      * '{value} is not a valid date - it must be in the format {format}').
38965      */
38966     invalidText : "{0} is not a valid date - it must be in the format {1}",
38967     /**
38968      * @cfg {String} triggerClass
38969      * An additional CSS class used to style the trigger button.  The trigger will always get the
38970      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38971      * which displays a calendar icon).
38972      */
38973     triggerClass : 'x-form-date-trigger',
38974     
38975
38976     /**
38977      * @cfg {Boolean} useIso
38978      * if enabled, then the date field will use a hidden field to store the 
38979      * real value as iso formated date. default (true)
38980      */ 
38981     useIso : true,
38982     /**
38983      * @cfg {String/Object} autoCreate
38984      * A DomHelper element spec, or true for a default element spec (defaults to
38985      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38986      */ 
38987     // private
38988     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38989     
38990     // private
38991     hiddenField: false,
38992     
38993     hideMonthPicker : false,
38994     
38995     onRender : function(ct, position)
38996     {
38997         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38998         if (this.useIso) {
38999             this.el.dom.removeAttribute('name'); 
39000             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39001                     'before', true);
39002             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39003             // prevent input submission
39004             this.hiddenName = this.name;
39005         }
39006             
39007             
39008     },
39009     
39010     // private
39011     validateValue : function(value)
39012     {
39013         value = this.formatDate(value);
39014         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39015             return false;
39016         }
39017         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39018              return true;
39019         }
39020         var svalue = value;
39021         value = this.parseDate(value);
39022         if(!value){
39023             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39024             return false;
39025         }
39026         var time = value.getTime();
39027         if(this.minValue && time < this.minValue.getTime()){
39028             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39029             return false;
39030         }
39031         if(this.maxValue && time > this.maxValue.getTime()){
39032             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39033             return false;
39034         }
39035         /*if(this.disabledDays){
39036             var day = value.getDay();
39037             for(var i = 0; i < this.disabledDays.length; i++) {
39038                 if(day === this.disabledDays[i]){
39039                     this.markInvalid(this.disabledDaysText);
39040                     return false;
39041                 }
39042             }
39043         }
39044         */
39045         var fvalue = this.formatDate(value);
39046         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39047             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39048             return false;
39049         }
39050         */
39051         return true;
39052     },
39053
39054     // private
39055     // Provides logic to override the default TriggerField.validateBlur which just returns true
39056     validateBlur : function(){
39057         return !this.menu || !this.menu.isVisible();
39058     },
39059
39060     /**
39061      * Returns the current date value of the date field.
39062      * @return {Date} The date value
39063      */
39064     getValue : function(){
39065         
39066         
39067         
39068         return  this.hiddenField ?
39069                 this.hiddenField.value :
39070                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39071     },
39072
39073     /**
39074      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39075      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39076      * (the default format used is "m/d/y").
39077      * <br />Usage:
39078      * <pre><code>
39079 //All of these calls set the same date value (May 4, 2006)
39080
39081 //Pass a date object:
39082 var dt = new Date('5/4/06');
39083 monthField.setValue(dt);
39084
39085 //Pass a date string (default format):
39086 monthField.setValue('5/4/06');
39087
39088 //Pass a date string (custom format):
39089 monthField.format = 'Y-m-d';
39090 monthField.setValue('2006-5-4');
39091 </code></pre>
39092      * @param {String/Date} date The date or valid date string
39093      */
39094     setValue : function(date){
39095         Roo.log('month setValue' + date);
39096         // can only be first of month..
39097         
39098         var val = this.parseDate(date);
39099         
39100         if (this.hiddenField) {
39101             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39102         }
39103         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39104         this.value = this.parseDate(date);
39105     },
39106
39107     // private
39108     parseDate : function(value){
39109         if(!value || value instanceof Date){
39110             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39111             return value;
39112         }
39113         var v = Date.parseDate(value, this.format);
39114         if (!v && this.useIso) {
39115             v = Date.parseDate(value, 'Y-m-d');
39116         }
39117         if (v) {
39118             // 
39119             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39120         }
39121         
39122         
39123         if(!v && this.altFormats){
39124             if(!this.altFormatsArray){
39125                 this.altFormatsArray = this.altFormats.split("|");
39126             }
39127             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39128                 v = Date.parseDate(value, this.altFormatsArray[i]);
39129             }
39130         }
39131         return v;
39132     },
39133
39134     // private
39135     formatDate : function(date, fmt){
39136         return (!date || !(date instanceof Date)) ?
39137                date : date.dateFormat(fmt || this.format);
39138     },
39139
39140     // private
39141     menuListeners : {
39142         select: function(m, d){
39143             this.setValue(d);
39144             this.fireEvent('select', this, d);
39145         },
39146         show : function(){ // retain focus styling
39147             this.onFocus();
39148         },
39149         hide : function(){
39150             this.focus.defer(10, this);
39151             var ml = this.menuListeners;
39152             this.menu.un("select", ml.select,  this);
39153             this.menu.un("show", ml.show,  this);
39154             this.menu.un("hide", ml.hide,  this);
39155         }
39156     },
39157     // private
39158     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39159     onTriggerClick : function(){
39160         if(this.disabled){
39161             return;
39162         }
39163         if(this.menu == null){
39164             this.menu = new Roo.menu.DateMenu();
39165            
39166         }
39167         
39168         Roo.apply(this.menu.picker,  {
39169             
39170             showClear: this.allowBlank,
39171             minDate : this.minValue,
39172             maxDate : this.maxValue,
39173             disabledDatesRE : this.ddMatch,
39174             disabledDatesText : this.disabledDatesText,
39175             
39176             format : this.useIso ? 'Y-m-d' : this.format,
39177             minText : String.format(this.minText, this.formatDate(this.minValue)),
39178             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39179             
39180         });
39181          this.menu.on(Roo.apply({}, this.menuListeners, {
39182             scope:this
39183         }));
39184        
39185         
39186         var m = this.menu;
39187         var p = m.picker;
39188         
39189         // hide month picker get's called when we called by 'before hide';
39190         
39191         var ignorehide = true;
39192         p.hideMonthPicker  = function(disableAnim){
39193             if (ignorehide) {
39194                 return;
39195             }
39196              if(this.monthPicker){
39197                 Roo.log("hideMonthPicker called");
39198                 if(disableAnim === true){
39199                     this.monthPicker.hide();
39200                 }else{
39201                     this.monthPicker.slideOut('t', {duration:.2});
39202                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39203                     p.fireEvent("select", this, this.value);
39204                     m.hide();
39205                 }
39206             }
39207         }
39208         
39209         Roo.log('picker set value');
39210         Roo.log(this.getValue());
39211         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39212         m.show(this.el, 'tl-bl?');
39213         ignorehide  = false;
39214         // this will trigger hideMonthPicker..
39215         
39216         
39217         // hidden the day picker
39218         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39219         
39220         
39221         
39222       
39223         
39224         p.showMonthPicker.defer(100, p);
39225     
39226         
39227        
39228     },
39229
39230     beforeBlur : function(){
39231         var v = this.parseDate(this.getRawValue());
39232         if(v){
39233             this.setValue(v);
39234         }
39235     }
39236
39237     /** @cfg {Boolean} grow @hide */
39238     /** @cfg {Number} growMin @hide */
39239     /** @cfg {Number} growMax @hide */
39240     /**
39241      * @hide
39242      * @method autoSize
39243      */
39244 });/*
39245  * Based on:
39246  * Ext JS Library 1.1.1
39247  * Copyright(c) 2006-2007, Ext JS, LLC.
39248  *
39249  * Originally Released Under LGPL - original licence link has changed is not relivant.
39250  *
39251  * Fork - LGPL
39252  * <script type="text/javascript">
39253  */
39254  
39255
39256 /**
39257  * @class Roo.form.ComboBox
39258  * @extends Roo.form.TriggerField
39259  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39260  * @constructor
39261  * Create a new ComboBox.
39262  * @param {Object} config Configuration options
39263  */
39264 Roo.form.ComboBox = function(config){
39265     Roo.form.ComboBox.superclass.constructor.call(this, config);
39266     this.addEvents({
39267         /**
39268          * @event expand
39269          * Fires when the dropdown list is expanded
39270              * @param {Roo.form.ComboBox} combo This combo box
39271              */
39272         'expand' : true,
39273         /**
39274          * @event collapse
39275          * Fires when the dropdown list is collapsed
39276              * @param {Roo.form.ComboBox} combo This combo box
39277              */
39278         'collapse' : true,
39279         /**
39280          * @event beforeselect
39281          * Fires before a list item is selected. Return false to cancel the selection.
39282              * @param {Roo.form.ComboBox} combo This combo box
39283              * @param {Roo.data.Record} record The data record returned from the underlying store
39284              * @param {Number} index The index of the selected item in the dropdown list
39285              */
39286         'beforeselect' : true,
39287         /**
39288          * @event select
39289          * Fires when a list item is selected
39290              * @param {Roo.form.ComboBox} combo This combo box
39291              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39292              * @param {Number} index The index of the selected item in the dropdown list
39293              */
39294         'select' : true,
39295         /**
39296          * @event beforequery
39297          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39298          * The event object passed has these properties:
39299              * @param {Roo.form.ComboBox} combo This combo box
39300              * @param {String} query The query
39301              * @param {Boolean} forceAll true to force "all" query
39302              * @param {Boolean} cancel true to cancel the query
39303              * @param {Object} e The query event object
39304              */
39305         'beforequery': true,
39306          /**
39307          * @event add
39308          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39309              * @param {Roo.form.ComboBox} combo This combo box
39310              */
39311         'add' : true,
39312         /**
39313          * @event edit
39314          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39315              * @param {Roo.form.ComboBox} combo This combo box
39316              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39317              */
39318         'edit' : true
39319         
39320         
39321     });
39322     if(this.transform){
39323         this.allowDomMove = false;
39324         var s = Roo.getDom(this.transform);
39325         if(!this.hiddenName){
39326             this.hiddenName = s.name;
39327         }
39328         if(!this.store){
39329             this.mode = 'local';
39330             var d = [], opts = s.options;
39331             for(var i = 0, len = opts.length;i < len; i++){
39332                 var o = opts[i];
39333                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39334                 if(o.selected) {
39335                     this.value = value;
39336                 }
39337                 d.push([value, o.text]);
39338             }
39339             this.store = new Roo.data.SimpleStore({
39340                 'id': 0,
39341                 fields: ['value', 'text'],
39342                 data : d
39343             });
39344             this.valueField = 'value';
39345             this.displayField = 'text';
39346         }
39347         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39348         if(!this.lazyRender){
39349             this.target = true;
39350             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39351             s.parentNode.removeChild(s); // remove it
39352             this.render(this.el.parentNode);
39353         }else{
39354             s.parentNode.removeChild(s); // remove it
39355         }
39356
39357     }
39358     if (this.store) {
39359         this.store = Roo.factory(this.store, Roo.data);
39360     }
39361     
39362     this.selectedIndex = -1;
39363     if(this.mode == 'local'){
39364         if(config.queryDelay === undefined){
39365             this.queryDelay = 10;
39366         }
39367         if(config.minChars === undefined){
39368             this.minChars = 0;
39369         }
39370     }
39371 };
39372
39373 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39374     /**
39375      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39376      */
39377     /**
39378      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39379      * rendering into an Roo.Editor, defaults to false)
39380      */
39381     /**
39382      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39383      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39384      */
39385     /**
39386      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39387      */
39388     /**
39389      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39390      * the dropdown list (defaults to undefined, with no header element)
39391      */
39392
39393      /**
39394      * @cfg {String/Roo.Template} tpl The template to use to render the output
39395      */
39396      
39397     // private
39398     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39399     /**
39400      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39401      */
39402     listWidth: undefined,
39403     /**
39404      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39405      * mode = 'remote' or 'text' if mode = 'local')
39406      */
39407     displayField: undefined,
39408     /**
39409      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39410      * mode = 'remote' or 'value' if mode = 'local'). 
39411      * Note: use of a valueField requires the user make a selection
39412      * in order for a value to be mapped.
39413      */
39414     valueField: undefined,
39415     
39416     
39417     /**
39418      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39419      * field's data value (defaults to the underlying DOM element's name)
39420      */
39421     hiddenName: undefined,
39422     /**
39423      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39424      */
39425     listClass: '',
39426     /**
39427      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39428      */
39429     selectedClass: 'x-combo-selected',
39430     /**
39431      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39432      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39433      * which displays a downward arrow icon).
39434      */
39435     triggerClass : 'x-form-arrow-trigger',
39436     /**
39437      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39438      */
39439     shadow:'sides',
39440     /**
39441      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39442      * anchor positions (defaults to 'tl-bl')
39443      */
39444     listAlign: 'tl-bl?',
39445     /**
39446      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39447      */
39448     maxHeight: 300,
39449     /**
39450      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39451      * query specified by the allQuery config option (defaults to 'query')
39452      */
39453     triggerAction: 'query',
39454     /**
39455      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39456      * (defaults to 4, does not apply if editable = false)
39457      */
39458     minChars : 4,
39459     /**
39460      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39461      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39462      */
39463     typeAhead: false,
39464     /**
39465      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39466      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39467      */
39468     queryDelay: 500,
39469     /**
39470      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39471      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39472      */
39473     pageSize: 0,
39474     /**
39475      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39476      * when editable = true (defaults to false)
39477      */
39478     selectOnFocus:false,
39479     /**
39480      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39481      */
39482     queryParam: 'query',
39483     /**
39484      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39485      * when mode = 'remote' (defaults to 'Loading...')
39486      */
39487     loadingText: 'Loading...',
39488     /**
39489      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39490      */
39491     resizable: false,
39492     /**
39493      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39494      */
39495     handleHeight : 8,
39496     /**
39497      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39498      * traditional select (defaults to true)
39499      */
39500     editable: true,
39501     /**
39502      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39503      */
39504     allQuery: '',
39505     /**
39506      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39507      */
39508     mode: 'remote',
39509     /**
39510      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39511      * listWidth has a higher value)
39512      */
39513     minListWidth : 70,
39514     /**
39515      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39516      * allow the user to set arbitrary text into the field (defaults to false)
39517      */
39518     forceSelection:false,
39519     /**
39520      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39521      * if typeAhead = true (defaults to 250)
39522      */
39523     typeAheadDelay : 250,
39524     /**
39525      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39526      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39527      */
39528     valueNotFoundText : undefined,
39529     /**
39530      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39531      */
39532     blockFocus : false,
39533     
39534     /**
39535      * @cfg {Boolean} disableClear Disable showing of clear button.
39536      */
39537     disableClear : false,
39538     /**
39539      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39540      */
39541     alwaysQuery : false,
39542     
39543     //private
39544     addicon : false,
39545     editicon: false,
39546     
39547     // element that contains real text value.. (when hidden is used..)
39548      
39549     // private
39550     onRender : function(ct, position){
39551         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39552         if(this.hiddenName){
39553             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39554                     'before', true);
39555             this.hiddenField.value =
39556                 this.hiddenValue !== undefined ? this.hiddenValue :
39557                 this.value !== undefined ? this.value : '';
39558
39559             // prevent input submission
39560             this.el.dom.removeAttribute('name');
39561              
39562              
39563         }
39564         if(Roo.isGecko){
39565             this.el.dom.setAttribute('autocomplete', 'off');
39566         }
39567
39568         var cls = 'x-combo-list';
39569
39570         this.list = new Roo.Layer({
39571             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39572         });
39573
39574         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39575         this.list.setWidth(lw);
39576         this.list.swallowEvent('mousewheel');
39577         this.assetHeight = 0;
39578
39579         if(this.title){
39580             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39581             this.assetHeight += this.header.getHeight();
39582         }
39583
39584         this.innerList = this.list.createChild({cls:cls+'-inner'});
39585         this.innerList.on('mouseover', this.onViewOver, this);
39586         this.innerList.on('mousemove', this.onViewMove, this);
39587         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39588         
39589         if(this.allowBlank && !this.pageSize && !this.disableClear){
39590             this.footer = this.list.createChild({cls:cls+'-ft'});
39591             this.pageTb = new Roo.Toolbar(this.footer);
39592            
39593         }
39594         if(this.pageSize){
39595             this.footer = this.list.createChild({cls:cls+'-ft'});
39596             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39597                     {pageSize: this.pageSize});
39598             
39599         }
39600         
39601         if (this.pageTb && this.allowBlank && !this.disableClear) {
39602             var _this = this;
39603             this.pageTb.add(new Roo.Toolbar.Fill(), {
39604                 cls: 'x-btn-icon x-btn-clear',
39605                 text: '&#160;',
39606                 handler: function()
39607                 {
39608                     _this.collapse();
39609                     _this.clearValue();
39610                     _this.onSelect(false, -1);
39611                 }
39612             });
39613         }
39614         if (this.footer) {
39615             this.assetHeight += this.footer.getHeight();
39616         }
39617         
39618
39619         if(!this.tpl){
39620             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39621         }
39622
39623         this.view = new Roo.View(this.innerList, this.tpl, {
39624             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39625         });
39626
39627         this.view.on('click', this.onViewClick, this);
39628
39629         this.store.on('beforeload', this.onBeforeLoad, this);
39630         this.store.on('load', this.onLoad, this);
39631         this.store.on('loadexception', this.onLoadException, this);
39632
39633         if(this.resizable){
39634             this.resizer = new Roo.Resizable(this.list,  {
39635                pinned:true, handles:'se'
39636             });
39637             this.resizer.on('resize', function(r, w, h){
39638                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39639                 this.listWidth = w;
39640                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39641                 this.restrictHeight();
39642             }, this);
39643             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39644         }
39645         if(!this.editable){
39646             this.editable = true;
39647             this.setEditable(false);
39648         }  
39649         
39650         
39651         if (typeof(this.events.add.listeners) != 'undefined') {
39652             
39653             this.addicon = this.wrap.createChild(
39654                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39655        
39656             this.addicon.on('click', function(e) {
39657                 this.fireEvent('add', this);
39658             }, this);
39659         }
39660         if (typeof(this.events.edit.listeners) != 'undefined') {
39661             
39662             this.editicon = this.wrap.createChild(
39663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39664             if (this.addicon) {
39665                 this.editicon.setStyle('margin-left', '40px');
39666             }
39667             this.editicon.on('click', function(e) {
39668                 
39669                 // we fire even  if inothing is selected..
39670                 this.fireEvent('edit', this, this.lastData );
39671                 
39672             }, this);
39673         }
39674         
39675         
39676         
39677     },
39678
39679     // private
39680     initEvents : function(){
39681         Roo.form.ComboBox.superclass.initEvents.call(this);
39682
39683         this.keyNav = new Roo.KeyNav(this.el, {
39684             "up" : function(e){
39685                 this.inKeyMode = true;
39686                 this.selectPrev();
39687             },
39688
39689             "down" : function(e){
39690                 if(!this.isExpanded()){
39691                     this.onTriggerClick();
39692                 }else{
39693                     this.inKeyMode = true;
39694                     this.selectNext();
39695                 }
39696             },
39697
39698             "enter" : function(e){
39699                 this.onViewClick();
39700                 //return true;
39701             },
39702
39703             "esc" : function(e){
39704                 this.collapse();
39705             },
39706
39707             "tab" : function(e){
39708                 this.onViewClick(false);
39709                 this.fireEvent("specialkey", this, e);
39710                 return true;
39711             },
39712
39713             scope : this,
39714
39715             doRelay : function(foo, bar, hname){
39716                 if(hname == 'down' || this.scope.isExpanded()){
39717                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39718                 }
39719                 return true;
39720             },
39721
39722             forceKeyDown: true
39723         });
39724         this.queryDelay = Math.max(this.queryDelay || 10,
39725                 this.mode == 'local' ? 10 : 250);
39726         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39727         if(this.typeAhead){
39728             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39729         }
39730         if(this.editable !== false){
39731             this.el.on("keyup", this.onKeyUp, this);
39732         }
39733         if(this.forceSelection){
39734             this.on('blur', this.doForce, this);
39735         }
39736     },
39737
39738     onDestroy : function(){
39739         if(this.view){
39740             this.view.setStore(null);
39741             this.view.el.removeAllListeners();
39742             this.view.el.remove();
39743             this.view.purgeListeners();
39744         }
39745         if(this.list){
39746             this.list.destroy();
39747         }
39748         if(this.store){
39749             this.store.un('beforeload', this.onBeforeLoad, this);
39750             this.store.un('load', this.onLoad, this);
39751             this.store.un('loadexception', this.onLoadException, this);
39752         }
39753         Roo.form.ComboBox.superclass.onDestroy.call(this);
39754     },
39755
39756     // private
39757     fireKey : function(e){
39758         if(e.isNavKeyPress() && !this.list.isVisible()){
39759             this.fireEvent("specialkey", this, e);
39760         }
39761     },
39762
39763     // private
39764     onResize: function(w, h){
39765         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39766         
39767         if(typeof w != 'number'){
39768             // we do not handle it!?!?
39769             return;
39770         }
39771         var tw = this.trigger.getWidth();
39772         tw += this.addicon ? this.addicon.getWidth() : 0;
39773         tw += this.editicon ? this.editicon.getWidth() : 0;
39774         var x = w - tw;
39775         this.el.setWidth( this.adjustWidth('input', x));
39776             
39777         this.trigger.setStyle('left', x+'px');
39778         
39779         if(this.list && this.listWidth === undefined){
39780             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39781             this.list.setWidth(lw);
39782             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39783         }
39784         
39785     
39786         
39787     },
39788
39789     /**
39790      * Allow or prevent the user from directly editing the field text.  If false is passed,
39791      * the user will only be able to select from the items defined in the dropdown list.  This method
39792      * is the runtime equivalent of setting the 'editable' config option at config time.
39793      * @param {Boolean} value True to allow the user to directly edit the field text
39794      */
39795     setEditable : function(value){
39796         if(value == this.editable){
39797             return;
39798         }
39799         this.editable = value;
39800         if(!value){
39801             this.el.dom.setAttribute('readOnly', true);
39802             this.el.on('mousedown', this.onTriggerClick,  this);
39803             this.el.addClass('x-combo-noedit');
39804         }else{
39805             this.el.dom.setAttribute('readOnly', false);
39806             this.el.un('mousedown', this.onTriggerClick,  this);
39807             this.el.removeClass('x-combo-noedit');
39808         }
39809     },
39810
39811     // private
39812     onBeforeLoad : function(){
39813         if(!this.hasFocus){
39814             return;
39815         }
39816         this.innerList.update(this.loadingText ?
39817                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39818         this.restrictHeight();
39819         this.selectedIndex = -1;
39820     },
39821
39822     // private
39823     onLoad : function(){
39824         if(!this.hasFocus){
39825             return;
39826         }
39827         if(this.store.getCount() > 0){
39828             this.expand();
39829             this.restrictHeight();
39830             if(this.lastQuery == this.allQuery){
39831                 if(this.editable){
39832                     this.el.dom.select();
39833                 }
39834                 if(!this.selectByValue(this.value, true)){
39835                     this.select(0, true);
39836                 }
39837             }else{
39838                 this.selectNext();
39839                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39840                     this.taTask.delay(this.typeAheadDelay);
39841                 }
39842             }
39843         }else{
39844             this.onEmptyResults();
39845         }
39846         //this.el.focus();
39847     },
39848     // private
39849     onLoadException : function()
39850     {
39851         this.collapse();
39852         Roo.log(this.store.reader.jsonData);
39853         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39854             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39855         }
39856         
39857         
39858     },
39859     // private
39860     onTypeAhead : function(){
39861         if(this.store.getCount() > 0){
39862             var r = this.store.getAt(0);
39863             var newValue = r.data[this.displayField];
39864             var len = newValue.length;
39865             var selStart = this.getRawValue().length;
39866             if(selStart != len){
39867                 this.setRawValue(newValue);
39868                 this.selectText(selStart, newValue.length);
39869             }
39870         }
39871     },
39872
39873     // private
39874     onSelect : function(record, index){
39875         if(this.fireEvent('beforeselect', this, record, index) !== false){
39876             this.setFromData(index > -1 ? record.data : false);
39877             this.collapse();
39878             this.fireEvent('select', this, record, index);
39879         }
39880     },
39881
39882     /**
39883      * Returns the currently selected field value or empty string if no value is set.
39884      * @return {String} value The selected value
39885      */
39886     getValue : function(){
39887         if(this.valueField){
39888             return typeof this.value != 'undefined' ? this.value : '';
39889         }
39890         return Roo.form.ComboBox.superclass.getValue.call(this);
39891     },
39892
39893     /**
39894      * Clears any text/value currently set in the field
39895      */
39896     clearValue : function(){
39897         if(this.hiddenField){
39898             this.hiddenField.value = '';
39899         }
39900         this.value = '';
39901         this.setRawValue('');
39902         this.lastSelectionText = '';
39903         
39904     },
39905
39906     /**
39907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39908      * will be displayed in the field.  If the value does not match the data value of an existing item,
39909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39910      * Otherwise the field will be blank (although the value will still be set).
39911      * @param {String} value The value to match
39912      */
39913     setValue : function(v){
39914         var text = v;
39915         if(this.valueField){
39916             var r = this.findRecord(this.valueField, v);
39917             if(r){
39918                 text = r.data[this.displayField];
39919             }else if(this.valueNotFoundText !== undefined){
39920                 text = this.valueNotFoundText;
39921             }
39922         }
39923         this.lastSelectionText = text;
39924         if(this.hiddenField){
39925             this.hiddenField.value = v;
39926         }
39927         Roo.form.ComboBox.superclass.setValue.call(this, text);
39928         this.value = v;
39929     },
39930     /**
39931      * @property {Object} the last set data for the element
39932      */
39933     
39934     lastData : false,
39935     /**
39936      * Sets the value of the field based on a object which is related to the record format for the store.
39937      * @param {Object} value the value to set as. or false on reset?
39938      */
39939     setFromData : function(o){
39940         var dv = ''; // display value
39941         var vv = ''; // value value..
39942         this.lastData = o;
39943         if (this.displayField) {
39944             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39945         } else {
39946             // this is an error condition!!!
39947             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39948         }
39949         
39950         if(this.valueField){
39951             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39952         }
39953         if(this.hiddenField){
39954             this.hiddenField.value = vv;
39955             
39956             this.lastSelectionText = dv;
39957             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39958             this.value = vv;
39959             return;
39960         }
39961         // no hidden field.. - we store the value in 'value', but still display
39962         // display field!!!!
39963         this.lastSelectionText = dv;
39964         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39965         this.value = vv;
39966         
39967         
39968     },
39969     // private
39970     reset : function(){
39971         // overridden so that last data is reset..
39972         this.setValue(this.resetValue);
39973         this.clearInvalid();
39974         this.lastData = false;
39975         if (this.view) {
39976             this.view.clearSelections();
39977         }
39978     },
39979     // private
39980     findRecord : function(prop, value){
39981         var record;
39982         if(this.store.getCount() > 0){
39983             this.store.each(function(r){
39984                 if(r.data[prop] == value){
39985                     record = r;
39986                     return false;
39987                 }
39988                 return true;
39989             });
39990         }
39991         return record;
39992     },
39993     
39994     getName: function()
39995     {
39996         // returns hidden if it's set..
39997         if (!this.rendered) {return ''};
39998         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39999         
40000     },
40001     // private
40002     onViewMove : function(e, t){
40003         this.inKeyMode = false;
40004     },
40005
40006     // private
40007     onViewOver : function(e, t){
40008         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40009             return;
40010         }
40011         var item = this.view.findItemFromChild(t);
40012         if(item){
40013             var index = this.view.indexOf(item);
40014             this.select(index, false);
40015         }
40016     },
40017
40018     // private
40019     onViewClick : function(doFocus)
40020     {
40021         var index = this.view.getSelectedIndexes()[0];
40022         var r = this.store.getAt(index);
40023         if(r){
40024             this.onSelect(r, index);
40025         }
40026         if(doFocus !== false && !this.blockFocus){
40027             this.el.focus();
40028         }
40029     },
40030
40031     // private
40032     restrictHeight : function(){
40033         this.innerList.dom.style.height = '';
40034         var inner = this.innerList.dom;
40035         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40036         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40037         this.list.beginUpdate();
40038         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40039         this.list.alignTo(this.el, this.listAlign);
40040         this.list.endUpdate();
40041     },
40042
40043     // private
40044     onEmptyResults : function(){
40045         this.collapse();
40046     },
40047
40048     /**
40049      * Returns true if the dropdown list is expanded, else false.
40050      */
40051     isExpanded : function(){
40052         return this.list.isVisible();
40053     },
40054
40055     /**
40056      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40058      * @param {String} value The data value of the item to select
40059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40060      * selected item if it is not currently in view (defaults to true)
40061      * @return {Boolean} True if the value matched an item in the list, else false
40062      */
40063     selectByValue : function(v, scrollIntoView){
40064         if(v !== undefined && v !== null){
40065             var r = this.findRecord(this.valueField || this.displayField, v);
40066             if(r){
40067                 this.select(this.store.indexOf(r), scrollIntoView);
40068                 return true;
40069             }
40070         }
40071         return false;
40072     },
40073
40074     /**
40075      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40076      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40077      * @param {Number} index The zero-based index of the list item to select
40078      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40079      * selected item if it is not currently in view (defaults to true)
40080      */
40081     select : function(index, scrollIntoView){
40082         this.selectedIndex = index;
40083         this.view.select(index);
40084         if(scrollIntoView !== false){
40085             var el = this.view.getNode(index);
40086             if(el){
40087                 this.innerList.scrollChildIntoView(el, false);
40088             }
40089         }
40090     },
40091
40092     // private
40093     selectNext : function(){
40094         var ct = this.store.getCount();
40095         if(ct > 0){
40096             if(this.selectedIndex == -1){
40097                 this.select(0);
40098             }else if(this.selectedIndex < ct-1){
40099                 this.select(this.selectedIndex+1);
40100             }
40101         }
40102     },
40103
40104     // private
40105     selectPrev : function(){
40106         var ct = this.store.getCount();
40107         if(ct > 0){
40108             if(this.selectedIndex == -1){
40109                 this.select(0);
40110             }else if(this.selectedIndex != 0){
40111                 this.select(this.selectedIndex-1);
40112             }
40113         }
40114     },
40115
40116     // private
40117     onKeyUp : function(e){
40118         if(this.editable !== false && !e.isSpecialKey()){
40119             this.lastKey = e.getKey();
40120             this.dqTask.delay(this.queryDelay);
40121         }
40122     },
40123
40124     // private
40125     validateBlur : function(){
40126         return !this.list || !this.list.isVisible();   
40127     },
40128
40129     // private
40130     initQuery : function(){
40131         this.doQuery(this.getRawValue());
40132     },
40133
40134     // private
40135     doForce : function(){
40136         if(this.el.dom.value.length > 0){
40137             this.el.dom.value =
40138                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40139              
40140         }
40141     },
40142
40143     /**
40144      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40145      * query allowing the query action to be canceled if needed.
40146      * @param {String} query The SQL query to execute
40147      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40148      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40149      * saved in the current store (defaults to false)
40150      */
40151     doQuery : function(q, forceAll){
40152         if(q === undefined || q === null){
40153             q = '';
40154         }
40155         var qe = {
40156             query: q,
40157             forceAll: forceAll,
40158             combo: this,
40159             cancel:false
40160         };
40161         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40162             return false;
40163         }
40164         q = qe.query;
40165         forceAll = qe.forceAll;
40166         if(forceAll === true || (q.length >= this.minChars)){
40167             if(this.lastQuery != q || this.alwaysQuery){
40168                 this.lastQuery = q;
40169                 if(this.mode == 'local'){
40170                     this.selectedIndex = -1;
40171                     if(forceAll){
40172                         this.store.clearFilter();
40173                     }else{
40174                         this.store.filter(this.displayField, q);
40175                     }
40176                     this.onLoad();
40177                 }else{
40178                     this.store.baseParams[this.queryParam] = q;
40179                     this.store.load({
40180                         params: this.getParams(q)
40181                     });
40182                     this.expand();
40183                 }
40184             }else{
40185                 this.selectedIndex = -1;
40186                 this.onLoad();   
40187             }
40188         }
40189     },
40190
40191     // private
40192     getParams : function(q){
40193         var p = {};
40194         //p[this.queryParam] = q;
40195         if(this.pageSize){
40196             p.start = 0;
40197             p.limit = this.pageSize;
40198         }
40199         return p;
40200     },
40201
40202     /**
40203      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40204      */
40205     collapse : function(){
40206         if(!this.isExpanded()){
40207             return;
40208         }
40209         this.list.hide();
40210         Roo.get(document).un('mousedown', this.collapseIf, this);
40211         Roo.get(document).un('mousewheel', this.collapseIf, this);
40212         if (!this.editable) {
40213             Roo.get(document).un('keydown', this.listKeyPress, this);
40214         }
40215         this.fireEvent('collapse', this);
40216     },
40217
40218     // private
40219     collapseIf : function(e){
40220         if(!e.within(this.wrap) && !e.within(this.list)){
40221             this.collapse();
40222         }
40223     },
40224
40225     /**
40226      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40227      */
40228     expand : function(){
40229         if(this.isExpanded() || !this.hasFocus){
40230             return;
40231         }
40232         this.list.alignTo(this.el, this.listAlign);
40233         this.list.show();
40234         Roo.get(document).on('mousedown', this.collapseIf, this);
40235         Roo.get(document).on('mousewheel', this.collapseIf, this);
40236         if (!this.editable) {
40237             Roo.get(document).on('keydown', this.listKeyPress, this);
40238         }
40239         
40240         this.fireEvent('expand', this);
40241     },
40242
40243     // private
40244     // Implements the default empty TriggerField.onTriggerClick function
40245     onTriggerClick : function(){
40246         if(this.disabled){
40247             return;
40248         }
40249         if(this.isExpanded()){
40250             this.collapse();
40251             if (!this.blockFocus) {
40252                 this.el.focus();
40253             }
40254             
40255         }else {
40256             this.hasFocus = true;
40257             if(this.triggerAction == 'all') {
40258                 this.doQuery(this.allQuery, true);
40259             } else {
40260                 this.doQuery(this.getRawValue());
40261             }
40262             if (!this.blockFocus) {
40263                 this.el.focus();
40264             }
40265         }
40266     },
40267     listKeyPress : function(e)
40268     {
40269         //Roo.log('listkeypress');
40270         // scroll to first matching element based on key pres..
40271         if (e.isSpecialKey()) {
40272             return false;
40273         }
40274         var k = String.fromCharCode(e.getKey()).toUpperCase();
40275         //Roo.log(k);
40276         var match  = false;
40277         var csel = this.view.getSelectedNodes();
40278         var cselitem = false;
40279         if (csel.length) {
40280             var ix = this.view.indexOf(csel[0]);
40281             cselitem  = this.store.getAt(ix);
40282             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40283                 cselitem = false;
40284             }
40285             
40286         }
40287         
40288         this.store.each(function(v) { 
40289             if (cselitem) {
40290                 // start at existing selection.
40291                 if (cselitem.id == v.id) {
40292                     cselitem = false;
40293                 }
40294                 return;
40295             }
40296                 
40297             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40298                 match = this.store.indexOf(v);
40299                 return false;
40300             }
40301         }, this);
40302         
40303         if (match === false) {
40304             return true; // no more action?
40305         }
40306         // scroll to?
40307         this.view.select(match);
40308         var sn = Roo.get(this.view.getSelectedNodes()[0])
40309         sn.scrollIntoView(sn.dom.parentNode, false);
40310     }
40311
40312     /** 
40313     * @cfg {Boolean} grow 
40314     * @hide 
40315     */
40316     /** 
40317     * @cfg {Number} growMin 
40318     * @hide 
40319     */
40320     /** 
40321     * @cfg {Number} growMax 
40322     * @hide 
40323     */
40324     /**
40325      * @hide
40326      * @method autoSize
40327      */
40328 });/*
40329  * Copyright(c) 2010-2012, Roo J Solutions Limited
40330  *
40331  * Licence LGPL
40332  *
40333  */
40334
40335 /**
40336  * @class Roo.form.ComboBoxArray
40337  * @extends Roo.form.TextField
40338  * A facebook style adder... for lists of email / people / countries  etc...
40339  * pick multiple items from a combo box, and shows each one.
40340  *
40341  *  Fred [x]  Brian [x]  [Pick another |v]
40342  *
40343  *
40344  *  For this to work: it needs various extra information
40345  *    - normal combo problay has
40346  *      name, hiddenName
40347  *    + displayField, valueField
40348  *
40349  *    For our purpose...
40350  *
40351  *
40352  *   If we change from 'extends' to wrapping...
40353  *   
40354  *  
40355  *
40356  
40357  
40358  * @constructor
40359  * Create a new ComboBoxArray.
40360  * @param {Object} config Configuration options
40361  */
40362  
40363
40364 Roo.form.ComboBoxArray = function(config)
40365 {
40366     this.addEvents({
40367         /**
40368          * @event beforeremove
40369          * Fires before remove the value from the list
40370              * @param {Roo.form.ComboBoxArray} _self This combo box array
40371              * @param {Roo.form.ComboBoxArray.Item} item removed item
40372              */
40373         'beforeremove' : true,
40374         /**
40375          * @event remove
40376          * Fires when remove the value from the list
40377              * @param {Roo.form.ComboBoxArray} _self This combo box array
40378              * @param {Roo.form.ComboBoxArray.Item} item removed item
40379              */
40380         'remove' : true
40381         
40382         
40383     });
40384     
40385     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40386     
40387     this.items = new Roo.util.MixedCollection(false);
40388     
40389     // construct the child combo...
40390     
40391     
40392     
40393     
40394    
40395     
40396 }
40397
40398  
40399 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40400
40401     /**
40402      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40403      */
40404     
40405     lastData : false,
40406     
40407     // behavies liek a hiddne field
40408     inputType:      'hidden',
40409     /**
40410      * @cfg {Number} width The width of the box that displays the selected element
40411      */ 
40412     width:          300,
40413
40414     
40415     
40416     /**
40417      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40418      */
40419     name : false,
40420     /**
40421      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40422      */
40423     hiddenName : false,
40424     
40425     
40426     // private the array of items that are displayed..
40427     items  : false,
40428     // private - the hidden field el.
40429     hiddenEl : false,
40430     // private - the filed el..
40431     el : false,
40432     
40433     //validateValue : function() { return true; }, // all values are ok!
40434     //onAddClick: function() { },
40435     
40436     onRender : function(ct, position) 
40437     {
40438         
40439         // create the standard hidden element
40440         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40441         
40442         
40443         // give fake names to child combo;
40444         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40445         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40446         
40447         this.combo = Roo.factory(this.combo, Roo.form);
40448         this.combo.onRender(ct, position);
40449         if (typeof(this.combo.width) != 'undefined') {
40450             this.combo.onResize(this.combo.width,0);
40451         }
40452         
40453         this.combo.initEvents();
40454         
40455         // assigned so form know we need to do this..
40456         this.store          = this.combo.store;
40457         this.valueField     = this.combo.valueField;
40458         this.displayField   = this.combo.displayField ;
40459         
40460         
40461         this.combo.wrap.addClass('x-cbarray-grp');
40462         
40463         var cbwrap = this.combo.wrap.createChild(
40464             {tag: 'div', cls: 'x-cbarray-cb'},
40465             this.combo.el.dom
40466         );
40467         
40468              
40469         this.hiddenEl = this.combo.wrap.createChild({
40470             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40471         });
40472         this.el = this.combo.wrap.createChild({
40473             tag: 'input',  type:'hidden' , name: this.name, value : ''
40474         });
40475          //   this.el.dom.removeAttribute("name");
40476         
40477         
40478         this.outerWrap = this.combo.wrap;
40479         this.wrap = cbwrap;
40480         
40481         this.outerWrap.setWidth(this.width);
40482         this.outerWrap.dom.removeChild(this.el.dom);
40483         
40484         this.wrap.dom.appendChild(this.el.dom);
40485         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40486         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40487         
40488         this.combo.trigger.setStyle('position','relative');
40489         this.combo.trigger.setStyle('left', '0px');
40490         this.combo.trigger.setStyle('top', '2px');
40491         
40492         this.combo.el.setStyle('vertical-align', 'text-bottom');
40493         
40494         //this.trigger.setStyle('vertical-align', 'top');
40495         
40496         // this should use the code from combo really... on('add' ....)
40497         if (this.adder) {
40498             
40499         
40500             this.adder = this.outerWrap.createChild(
40501                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40502             var _t = this;
40503             this.adder.on('click', function(e) {
40504                 _t.fireEvent('adderclick', this, e);
40505             }, _t);
40506         }
40507         //var _t = this;
40508         //this.adder.on('click', this.onAddClick, _t);
40509         
40510         
40511         this.combo.on('select', function(cb, rec, ix) {
40512             this.addItem(rec.data);
40513             
40514             cb.setValue('');
40515             cb.el.dom.value = '';
40516             //cb.lastData = rec.data;
40517             // add to list
40518             
40519         }, this);
40520         
40521         
40522     },
40523     
40524     
40525     getName: function()
40526     {
40527         // returns hidden if it's set..
40528         if (!this.rendered) {return ''};
40529         return  this.hiddenName ? this.hiddenName : this.name;
40530         
40531     },
40532     
40533     
40534     onResize: function(w, h){
40535         
40536         return;
40537         // not sure if this is needed..
40538         //this.combo.onResize(w,h);
40539         
40540         if(typeof w != 'number'){
40541             // we do not handle it!?!?
40542             return;
40543         }
40544         var tw = this.combo.trigger.getWidth();
40545         tw += this.addicon ? this.addicon.getWidth() : 0;
40546         tw += this.editicon ? this.editicon.getWidth() : 0;
40547         var x = w - tw;
40548         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40549             
40550         this.combo.trigger.setStyle('left', '0px');
40551         
40552         if(this.list && this.listWidth === undefined){
40553             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40554             this.list.setWidth(lw);
40555             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40556         }
40557         
40558     
40559         
40560     },
40561     
40562     addItem: function(rec)
40563     {
40564         var valueField = this.combo.valueField;
40565         var displayField = this.combo.displayField;
40566         if (this.items.indexOfKey(rec[valueField]) > -1) {
40567             //console.log("GOT " + rec.data.id);
40568             return;
40569         }
40570         
40571         var x = new Roo.form.ComboBoxArray.Item({
40572             //id : rec[this.idField],
40573             data : rec,
40574             displayField : displayField ,
40575             tipField : displayField ,
40576             cb : this
40577         });
40578         // use the 
40579         this.items.add(rec[valueField],x);
40580         // add it before the element..
40581         this.updateHiddenEl();
40582         x.render(this.outerWrap, this.wrap.dom);
40583         // add the image handler..
40584     },
40585     
40586     updateHiddenEl : function()
40587     {
40588         this.validate();
40589         if (!this.hiddenEl) {
40590             return;
40591         }
40592         var ar = [];
40593         var idField = this.combo.valueField;
40594         
40595         this.items.each(function(f) {
40596             ar.push(f.data[idField]);
40597            
40598         });
40599         this.hiddenEl.dom.value = ar.join(',');
40600         this.validate();
40601     },
40602     
40603     reset : function()
40604     {
40605         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40606         this.items.each(function(f) {
40607            f.remove(); 
40608         });
40609         this.el.dom.value = '';
40610         if (this.hiddenEl) {
40611             this.hiddenEl.dom.value = '';
40612         }
40613         
40614     },
40615     getValue: function()
40616     {
40617         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40618     },
40619     setValue: function(v) // not a valid action - must use addItems..
40620     {
40621          
40622         this.reset();
40623         
40624         
40625         
40626         if (this.store.isLocal && (typeof(v) == 'string')) {
40627             // then we can use the store to find the values..
40628             // comma seperated at present.. this needs to allow JSON based encoding..
40629             this.hiddenEl.value  = v;
40630             var v_ar = [];
40631             Roo.each(v.split(','), function(k) {
40632                 Roo.log("CHECK " + this.valueField + ',' + k);
40633                 var li = this.store.query(this.valueField, k);
40634                 if (!li.length) {
40635                     return;
40636                 }
40637                 var add = {};
40638                 add[this.valueField] = k;
40639                 add[this.displayField] = li.item(0).data[this.displayField];
40640                 
40641                 this.addItem(add);
40642             }, this) 
40643              
40644         }
40645         if (typeof(v) == 'object' ) {
40646             // then let's assume it's an array of objects..
40647             Roo.each(v, function(l) {
40648                 this.addItem(l);
40649             }, this);
40650              
40651         }
40652         
40653         
40654     },
40655     setFromData: function(v)
40656     {
40657         // this recieves an object, if setValues is called.
40658         this.reset();
40659         this.el.dom.value = v[this.displayField];
40660         this.hiddenEl.dom.value = v[this.valueField];
40661         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40662             return;
40663         }
40664         var kv = v[this.valueField];
40665         var dv = v[this.displayField];
40666         kv = typeof(kv) != 'string' ? '' : kv;
40667         dv = typeof(dv) != 'string' ? '' : dv;
40668         
40669         
40670         var keys = kv.split(',');
40671         var display = dv.split(',');
40672         for (var i = 0 ; i < keys.length; i++) {
40673             
40674             add = {};
40675             add[this.valueField] = keys[i];
40676             add[this.displayField] = display[i];
40677             this.addItem(add);
40678         }
40679       
40680         
40681     },
40682     
40683     /**
40684      * Validates the combox array value
40685      * @return {Boolean} True if the value is valid, else false
40686      */
40687     validate : function(){
40688         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40689             this.clearInvalid();
40690             return true;
40691         }
40692         return false;
40693     },
40694     
40695     validateValue : function(value){
40696         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40697         
40698     },
40699     
40700     /*@
40701      * overide
40702      * 
40703      */
40704     isDirty : function() {
40705         if(this.disabled) {
40706             return false;
40707         }
40708         
40709         try {
40710             var d = Roo.decode(String(this.originalValue));
40711         } catch (e) {
40712             return String(this.getValue()) !== String(this.originalValue);
40713         }
40714         
40715         var originalValue = [];
40716         
40717         for (var i = 0; i < d.length; i++){
40718             originalValue.push(d[i][this.valueField]);
40719         }
40720         
40721         return String(this.getValue()) !== String(originalValue.join(','));
40722         
40723     }
40724     
40725 });
40726
40727
40728
40729 /**
40730  * @class Roo.form.ComboBoxArray.Item
40731  * @extends Roo.BoxComponent
40732  * A selected item in the list
40733  *  Fred [x]  Brian [x]  [Pick another |v]
40734  * 
40735  * @constructor
40736  * Create a new item.
40737  * @param {Object} config Configuration options
40738  */
40739  
40740 Roo.form.ComboBoxArray.Item = function(config) {
40741     config.id = Roo.id();
40742     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40743 }
40744
40745 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40746     data : {},
40747     cb: false,
40748     displayField : false,
40749     tipField : false,
40750     
40751     
40752     defaultAutoCreate : {
40753         tag: 'div',
40754         cls: 'x-cbarray-item',
40755         cn : [ 
40756             { tag: 'div' },
40757             {
40758                 tag: 'img',
40759                 width:16,
40760                 height : 16,
40761                 src : Roo.BLANK_IMAGE_URL ,
40762                 align: 'center'
40763             }
40764         ]
40765         
40766     },
40767     
40768  
40769     onRender : function(ct, position)
40770     {
40771         Roo.form.Field.superclass.onRender.call(this, ct, position);
40772         
40773         if(!this.el){
40774             var cfg = this.getAutoCreate();
40775             this.el = ct.createChild(cfg, position);
40776         }
40777         
40778         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40779         
40780         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40781             this.cb.renderer(this.data) :
40782             String.format('{0}',this.data[this.displayField]);
40783         
40784             
40785         this.el.child('div').dom.setAttribute('qtip',
40786                         String.format('{0}',this.data[this.tipField])
40787         );
40788         
40789         this.el.child('img').on('click', this.remove, this);
40790         
40791     },
40792    
40793     remove : function()
40794     {
40795         if(this.cb.disabled){
40796             return;
40797         }
40798         
40799         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40800             this.cb.items.remove(this);
40801             this.el.child('img').un('click', this.remove, this);
40802             this.el.remove();
40803             this.cb.updateHiddenEl();
40804
40805             this.cb.fireEvent('remove', this.cb, this);
40806         }
40807         
40808     }
40809 });/*
40810  * Based on:
40811  * Ext JS Library 1.1.1
40812  * Copyright(c) 2006-2007, Ext JS, LLC.
40813  *
40814  * Originally Released Under LGPL - original licence link has changed is not relivant.
40815  *
40816  * Fork - LGPL
40817  * <script type="text/javascript">
40818  */
40819 /**
40820  * @class Roo.form.Checkbox
40821  * @extends Roo.form.Field
40822  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40823  * @constructor
40824  * Creates a new Checkbox
40825  * @param {Object} config Configuration options
40826  */
40827 Roo.form.Checkbox = function(config){
40828     Roo.form.Checkbox.superclass.constructor.call(this, config);
40829     this.addEvents({
40830         /**
40831          * @event check
40832          * Fires when the checkbox is checked or unchecked.
40833              * @param {Roo.form.Checkbox} this This checkbox
40834              * @param {Boolean} checked The new checked value
40835              */
40836         check : true
40837     });
40838 };
40839
40840 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40841     /**
40842      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40843      */
40844     focusClass : undefined,
40845     /**
40846      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40847      */
40848     fieldClass: "x-form-field",
40849     /**
40850      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40851      */
40852     checked: false,
40853     /**
40854      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40855      * {tag: "input", type: "checkbox", autocomplete: "off"})
40856      */
40857     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40858     /**
40859      * @cfg {String} boxLabel The text that appears beside the checkbox
40860      */
40861     boxLabel : "",
40862     /**
40863      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40864      */  
40865     inputValue : '1',
40866     /**
40867      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40868      */
40869      valueOff: '0', // value when not checked..
40870
40871     actionMode : 'viewEl', 
40872     //
40873     // private
40874     itemCls : 'x-menu-check-item x-form-item',
40875     groupClass : 'x-menu-group-item',
40876     inputType : 'hidden',
40877     
40878     
40879     inSetChecked: false, // check that we are not calling self...
40880     
40881     inputElement: false, // real input element?
40882     basedOn: false, // ????
40883     
40884     isFormField: true, // not sure where this is needed!!!!
40885
40886     onResize : function(){
40887         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40888         if(!this.boxLabel){
40889             this.el.alignTo(this.wrap, 'c-c');
40890         }
40891     },
40892
40893     initEvents : function(){
40894         Roo.form.Checkbox.superclass.initEvents.call(this);
40895         this.el.on("click", this.onClick,  this);
40896         this.el.on("change", this.onClick,  this);
40897     },
40898
40899
40900     getResizeEl : function(){
40901         return this.wrap;
40902     },
40903
40904     getPositionEl : function(){
40905         return this.wrap;
40906     },
40907
40908     // private
40909     onRender : function(ct, position){
40910         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40911         /*
40912         if(this.inputValue !== undefined){
40913             this.el.dom.value = this.inputValue;
40914         }
40915         */
40916         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40917         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40918         var viewEl = this.wrap.createChild({ 
40919             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40920         this.viewEl = viewEl;   
40921         this.wrap.on('click', this.onClick,  this); 
40922         
40923         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40924         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40925         
40926         
40927         
40928         if(this.boxLabel){
40929             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40930         //    viewEl.on('click', this.onClick,  this); 
40931         }
40932         //if(this.checked){
40933             this.setChecked(this.checked);
40934         //}else{
40935             //this.checked = this.el.dom;
40936         //}
40937
40938     },
40939
40940     // private
40941     initValue : Roo.emptyFn,
40942
40943     /**
40944      * Returns the checked state of the checkbox.
40945      * @return {Boolean} True if checked, else false
40946      */
40947     getValue : function(){
40948         if(this.el){
40949             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40950         }
40951         return this.valueOff;
40952         
40953     },
40954
40955         // private
40956     onClick : function(){ 
40957         if (this.disabled) {
40958             return;
40959         }
40960         this.setChecked(!this.checked);
40961
40962         //if(this.el.dom.checked != this.checked){
40963         //    this.setValue(this.el.dom.checked);
40964        // }
40965     },
40966
40967     /**
40968      * Sets the checked state of the checkbox.
40969      * On is always based on a string comparison between inputValue and the param.
40970      * @param {Boolean/String} value - the value to set 
40971      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40972      */
40973     setValue : function(v,suppressEvent){
40974         
40975         
40976         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40977         //if(this.el && this.el.dom){
40978         //    this.el.dom.checked = this.checked;
40979         //    this.el.dom.defaultChecked = this.checked;
40980         //}
40981         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40982         //this.fireEvent("check", this, this.checked);
40983     },
40984     // private..
40985     setChecked : function(state,suppressEvent)
40986     {
40987         if (this.inSetChecked) {
40988             this.checked = state;
40989             return;
40990         }
40991         
40992     
40993         if(this.wrap){
40994             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40995         }
40996         this.checked = state;
40997         if(suppressEvent !== true){
40998             this.fireEvent('check', this, state);
40999         }
41000         this.inSetChecked = true;
41001         this.el.dom.value = state ? this.inputValue : this.valueOff;
41002         this.inSetChecked = false;
41003         
41004     },
41005     // handle setting of hidden value by some other method!!?!?
41006     setFromHidden: function()
41007     {
41008         if(!this.el){
41009             return;
41010         }
41011         //console.log("SET FROM HIDDEN");
41012         //alert('setFrom hidden');
41013         this.setValue(this.el.dom.value);
41014     },
41015     
41016     onDestroy : function()
41017     {
41018         if(this.viewEl){
41019             Roo.get(this.viewEl).remove();
41020         }
41021          
41022         Roo.form.Checkbox.superclass.onDestroy.call(this);
41023     }
41024
41025 });/*
41026  * Based on:
41027  * Ext JS Library 1.1.1
41028  * Copyright(c) 2006-2007, Ext JS, LLC.
41029  *
41030  * Originally Released Under LGPL - original licence link has changed is not relivant.
41031  *
41032  * Fork - LGPL
41033  * <script type="text/javascript">
41034  */
41035  
41036 /**
41037  * @class Roo.form.Radio
41038  * @extends Roo.form.Checkbox
41039  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41040  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41041  * @constructor
41042  * Creates a new Radio
41043  * @param {Object} config Configuration options
41044  */
41045 Roo.form.Radio = function(){
41046     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41047 };
41048 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41049     inputType: 'radio',
41050
41051     /**
41052      * If this radio is part of a group, it will return the selected value
41053      * @return {String}
41054      */
41055     getGroupValue : function(){
41056         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41057     },
41058     
41059     
41060     onRender : function(ct, position){
41061         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41062         
41063         if(this.inputValue !== undefined){
41064             this.el.dom.value = this.inputValue;
41065         }
41066          
41067         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41068         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41069         //var viewEl = this.wrap.createChild({ 
41070         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41071         //this.viewEl = viewEl;   
41072         //this.wrap.on('click', this.onClick,  this); 
41073         
41074         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41075         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41076         
41077         
41078         
41079         if(this.boxLabel){
41080             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41081         //    viewEl.on('click', this.onClick,  this); 
41082         }
41083          if(this.checked){
41084             this.el.dom.checked =   'checked' ;
41085         }
41086          
41087     } 
41088     
41089     
41090 });//<script type="text/javascript">
41091
41092 /*
41093  * Based  Ext JS Library 1.1.1
41094  * Copyright(c) 2006-2007, Ext JS, LLC.
41095  * LGPL
41096  *
41097  */
41098  
41099 /**
41100  * @class Roo.HtmlEditorCore
41101  * @extends Roo.Component
41102  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41103  *
41104  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41105  */
41106
41107 Roo.HtmlEditorCore = function(config){
41108     
41109     
41110     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41111     
41112     
41113     this.addEvents({
41114         /**
41115          * @event initialize
41116          * Fires when the editor is fully initialized (including the iframe)
41117          * @param {Roo.HtmlEditorCore} this
41118          */
41119         initialize: true,
41120         /**
41121          * @event activate
41122          * Fires when the editor is first receives the focus. Any insertion must wait
41123          * until after this event.
41124          * @param {Roo.HtmlEditorCore} this
41125          */
41126         activate: true,
41127          /**
41128          * @event beforesync
41129          * Fires before the textarea is updated with content from the editor iframe. Return false
41130          * to cancel the sync.
41131          * @param {Roo.HtmlEditorCore} this
41132          * @param {String} html
41133          */
41134         beforesync: true,
41135          /**
41136          * @event beforepush
41137          * Fires before the iframe editor is updated with content from the textarea. Return false
41138          * to cancel the push.
41139          * @param {Roo.HtmlEditorCore} this
41140          * @param {String} html
41141          */
41142         beforepush: true,
41143          /**
41144          * @event sync
41145          * Fires when the textarea is updated with content from the editor iframe.
41146          * @param {Roo.HtmlEditorCore} this
41147          * @param {String} html
41148          */
41149         sync: true,
41150          /**
41151          * @event push
41152          * Fires when the iframe editor is updated with content from the textarea.
41153          * @param {Roo.HtmlEditorCore} this
41154          * @param {String} html
41155          */
41156         push: true,
41157         
41158         /**
41159          * @event editorevent
41160          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41161          * @param {Roo.HtmlEditorCore} this
41162          */
41163         editorevent: true
41164         
41165     });
41166     
41167     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41168     
41169     // defaults : white / black...
41170     this.applyBlacklists();
41171     
41172     
41173     
41174 };
41175
41176
41177 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41178
41179
41180      /**
41181      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41182      */
41183     
41184     owner : false,
41185     
41186      /**
41187      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41188      *                        Roo.resizable.
41189      */
41190     resizable : false,
41191      /**
41192      * @cfg {Number} height (in pixels)
41193      */   
41194     height: 300,
41195    /**
41196      * @cfg {Number} width (in pixels)
41197      */   
41198     width: 500,
41199     
41200     /**
41201      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41202      * 
41203      */
41204     stylesheets: false,
41205     
41206     // id of frame..
41207     frameId: false,
41208     
41209     // private properties
41210     validationEvent : false,
41211     deferHeight: true,
41212     initialized : false,
41213     activated : false,
41214     sourceEditMode : false,
41215     onFocus : Roo.emptyFn,
41216     iframePad:3,
41217     hideMode:'offsets',
41218     
41219     clearUp: true,
41220     
41221     // blacklist + whitelisted elements..
41222     black: false,
41223     white: false,
41224      
41225     
41226
41227     /**
41228      * Protected method that will not generally be called directly. It
41229      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41230      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41231      */
41232     getDocMarkup : function(){
41233         // body styles..
41234         var st = '';
41235         
41236         // inherit styels from page...?? 
41237         if (this.stylesheets === false) {
41238             
41239             Roo.get(document.head).select('style').each(function(node) {
41240                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41241             });
41242             
41243             Roo.get(document.head).select('link').each(function(node) { 
41244                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41245             });
41246             
41247         } else if (!this.stylesheets.length) {
41248                 // simple..
41249                 st = '<style type="text/css">' +
41250                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41251                    '</style>';
41252         } else { 
41253             
41254         }
41255         
41256         st +=  '<style type="text/css">' +
41257             'IMG { cursor: pointer } ' +
41258         '</style>';
41259
41260         
41261         return '<html><head>' + st  +
41262             //<style type="text/css">' +
41263             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41264             //'</style>' +
41265             ' </head><body class="roo-htmleditor-body"></body></html>';
41266     },
41267
41268     // private
41269     onRender : function(ct, position)
41270     {
41271         var _t = this;
41272         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41273         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41274         
41275         
41276         this.el.dom.style.border = '0 none';
41277         this.el.dom.setAttribute('tabIndex', -1);
41278         this.el.addClass('x-hidden hide');
41279         
41280         
41281         
41282         if(Roo.isIE){ // fix IE 1px bogus margin
41283             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41284         }
41285        
41286         
41287         this.frameId = Roo.id();
41288         
41289          
41290         
41291         var iframe = this.owner.wrap.createChild({
41292             tag: 'iframe',
41293             cls: 'form-control', // bootstrap..
41294             id: this.frameId,
41295             name: this.frameId,
41296             frameBorder : 'no',
41297             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41298         }, this.el
41299         );
41300         
41301         
41302         this.iframe = iframe.dom;
41303
41304          this.assignDocWin();
41305         
41306         this.doc.designMode = 'on';
41307        
41308         this.doc.open();
41309         this.doc.write(this.getDocMarkup());
41310         this.doc.close();
41311
41312         
41313         var task = { // must defer to wait for browser to be ready
41314             run : function(){
41315                 //console.log("run task?" + this.doc.readyState);
41316                 this.assignDocWin();
41317                 if(this.doc.body || this.doc.readyState == 'complete'){
41318                     try {
41319                         this.doc.designMode="on";
41320                     } catch (e) {
41321                         return;
41322                     }
41323                     Roo.TaskMgr.stop(task);
41324                     this.initEditor.defer(10, this);
41325                 }
41326             },
41327             interval : 10,
41328             duration: 10000,
41329             scope: this
41330         };
41331         Roo.TaskMgr.start(task);
41332
41333     },
41334
41335     // private
41336     onResize : function(w, h)
41337     {
41338          Roo.log('resize: ' +w + ',' + h );
41339         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41340         if(!this.iframe){
41341             return;
41342         }
41343         if(typeof w == 'number'){
41344             
41345             this.iframe.style.width = w + 'px';
41346         }
41347         if(typeof h == 'number'){
41348             
41349             this.iframe.style.height = h + 'px';
41350             if(this.doc){
41351                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41352             }
41353         }
41354         
41355     },
41356
41357     /**
41358      * Toggles the editor between standard and source edit mode.
41359      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41360      */
41361     toggleSourceEdit : function(sourceEditMode){
41362         
41363         this.sourceEditMode = sourceEditMode === true;
41364         
41365         if(this.sourceEditMode){
41366  
41367             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41368             
41369         }else{
41370             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41371             //this.iframe.className = '';
41372             this.deferFocus();
41373         }
41374         //this.setSize(this.owner.wrap.getSize());
41375         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41376     },
41377
41378     
41379   
41380
41381     /**
41382      * Protected method that will not generally be called directly. If you need/want
41383      * custom HTML cleanup, this is the method you should override.
41384      * @param {String} html The HTML to be cleaned
41385      * return {String} The cleaned HTML
41386      */
41387     cleanHtml : function(html){
41388         html = String(html);
41389         if(html.length > 5){
41390             if(Roo.isSafari){ // strip safari nonsense
41391                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41392             }
41393         }
41394         if(html == '&nbsp;'){
41395             html = '';
41396         }
41397         return html;
41398     },
41399
41400     /**
41401      * HTML Editor -> Textarea
41402      * Protected method that will not generally be called directly. Syncs the contents
41403      * of the editor iframe with the textarea.
41404      */
41405     syncValue : function(){
41406         if(this.initialized){
41407             var bd = (this.doc.body || this.doc.documentElement);
41408             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41409             var html = bd.innerHTML;
41410             if(Roo.isSafari){
41411                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41412                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41413                 if(m && m[1]){
41414                     html = '<div style="'+m[0]+'">' + html + '</div>';
41415                 }
41416             }
41417             html = this.cleanHtml(html);
41418             // fix up the special chars.. normaly like back quotes in word...
41419             // however we do not want to do this with chinese..
41420             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41421                 var cc = b.charCodeAt();
41422                 if (
41423                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41424                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41425                     (cc >= 0xf900 && cc < 0xfb00 )
41426                 ) {
41427                         return b;
41428                 }
41429                 return "&#"+cc+";" 
41430             });
41431             if(this.owner.fireEvent('beforesync', this, html) !== false){
41432                 this.el.dom.value = html;
41433                 this.owner.fireEvent('sync', this, html);
41434             }
41435         }
41436     },
41437
41438     /**
41439      * Protected method that will not generally be called directly. Pushes the value of the textarea
41440      * into the iframe editor.
41441      */
41442     pushValue : function(){
41443         if(this.initialized){
41444             var v = this.el.dom.value.trim();
41445             
41446 //            if(v.length < 1){
41447 //                v = '&#160;';
41448 //            }
41449             
41450             if(this.owner.fireEvent('beforepush', this, v) !== false){
41451                 var d = (this.doc.body || this.doc.documentElement);
41452                 d.innerHTML = v;
41453                 this.cleanUpPaste();
41454                 this.el.dom.value = d.innerHTML;
41455                 this.owner.fireEvent('push', this, v);
41456             }
41457         }
41458     },
41459
41460     // private
41461     deferFocus : function(){
41462         this.focus.defer(10, this);
41463     },
41464
41465     // doc'ed in Field
41466     focus : function(){
41467         if(this.win && !this.sourceEditMode){
41468             this.win.focus();
41469         }else{
41470             this.el.focus();
41471         }
41472     },
41473     
41474     assignDocWin: function()
41475     {
41476         var iframe = this.iframe;
41477         
41478          if(Roo.isIE){
41479             this.doc = iframe.contentWindow.document;
41480             this.win = iframe.contentWindow;
41481         } else {
41482 //            if (!Roo.get(this.frameId)) {
41483 //                return;
41484 //            }
41485 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41486 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41487             
41488             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41489                 return;
41490             }
41491             
41492             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41493             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41494         }
41495     },
41496     
41497     // private
41498     initEditor : function(){
41499         //console.log("INIT EDITOR");
41500         this.assignDocWin();
41501         
41502         
41503         
41504         this.doc.designMode="on";
41505         this.doc.open();
41506         this.doc.write(this.getDocMarkup());
41507         this.doc.close();
41508         
41509         var dbody = (this.doc.body || this.doc.documentElement);
41510         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41511         // this copies styles from the containing element into thsi one..
41512         // not sure why we need all of this..
41513         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41514         
41515         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41516         //ss['background-attachment'] = 'fixed'; // w3c
41517         dbody.bgProperties = 'fixed'; // ie
41518         //Roo.DomHelper.applyStyles(dbody, ss);
41519         Roo.EventManager.on(this.doc, {
41520             //'mousedown': this.onEditorEvent,
41521             'mouseup': this.onEditorEvent,
41522             'dblclick': this.onEditorEvent,
41523             'click': this.onEditorEvent,
41524             'keyup': this.onEditorEvent,
41525             buffer:100,
41526             scope: this
41527         });
41528         if(Roo.isGecko){
41529             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41530         }
41531         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41532             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41533         }
41534         this.initialized = true;
41535
41536         this.owner.fireEvent('initialize', this);
41537         this.pushValue();
41538     },
41539
41540     // private
41541     onDestroy : function(){
41542         
41543         
41544         
41545         if(this.rendered){
41546             
41547             //for (var i =0; i < this.toolbars.length;i++) {
41548             //    // fixme - ask toolbars for heights?
41549             //    this.toolbars[i].onDestroy();
41550            // }
41551             
41552             //this.wrap.dom.innerHTML = '';
41553             //this.wrap.remove();
41554         }
41555     },
41556
41557     // private
41558     onFirstFocus : function(){
41559         
41560         this.assignDocWin();
41561         
41562         
41563         this.activated = true;
41564          
41565     
41566         if(Roo.isGecko){ // prevent silly gecko errors
41567             this.win.focus();
41568             var s = this.win.getSelection();
41569             if(!s.focusNode || s.focusNode.nodeType != 3){
41570                 var r = s.getRangeAt(0);
41571                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41572                 r.collapse(true);
41573                 this.deferFocus();
41574             }
41575             try{
41576                 this.execCmd('useCSS', true);
41577                 this.execCmd('styleWithCSS', false);
41578             }catch(e){}
41579         }
41580         this.owner.fireEvent('activate', this);
41581     },
41582
41583     // private
41584     adjustFont: function(btn){
41585         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41586         //if(Roo.isSafari){ // safari
41587         //    adjust *= 2;
41588        // }
41589         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41590         if(Roo.isSafari){ // safari
41591             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41592             v =  (v < 10) ? 10 : v;
41593             v =  (v > 48) ? 48 : v;
41594             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41595             
41596         }
41597         
41598         
41599         v = Math.max(1, v+adjust);
41600         
41601         this.execCmd('FontSize', v  );
41602     },
41603
41604     onEditorEvent : function(e){
41605         this.owner.fireEvent('editorevent', this, e);
41606       //  this.updateToolbar();
41607         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41608     },
41609
41610     insertTag : function(tg)
41611     {
41612         // could be a bit smarter... -> wrap the current selected tRoo..
41613         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41614             
41615             range = this.createRange(this.getSelection());
41616             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41617             wrappingNode.appendChild(range.extractContents());
41618             range.insertNode(wrappingNode);
41619
41620             return;
41621             
41622             
41623             
41624         }
41625         this.execCmd("formatblock",   tg);
41626         
41627     },
41628     
41629     insertText : function(txt)
41630     {
41631         
41632         
41633         var range = this.createRange();
41634         range.deleteContents();
41635                //alert(Sender.getAttribute('label'));
41636                
41637         range.insertNode(this.doc.createTextNode(txt));
41638     } ,
41639     
41640      
41641
41642     /**
41643      * Executes a Midas editor command on the editor document and performs necessary focus and
41644      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41645      * @param {String} cmd The Midas command
41646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41647      */
41648     relayCmd : function(cmd, value){
41649         this.win.focus();
41650         this.execCmd(cmd, value);
41651         this.owner.fireEvent('editorevent', this);
41652         //this.updateToolbar();
41653         this.owner.deferFocus();
41654     },
41655
41656     /**
41657      * Executes a Midas editor command directly on the editor document.
41658      * For visual commands, you should use {@link #relayCmd} instead.
41659      * <b>This should only be called after the editor is initialized.</b>
41660      * @param {String} cmd The Midas command
41661      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41662      */
41663     execCmd : function(cmd, value){
41664         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41665         this.syncValue();
41666     },
41667  
41668  
41669    
41670     /**
41671      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41672      * to insert tRoo.
41673      * @param {String} text | dom node.. 
41674      */
41675     insertAtCursor : function(text)
41676     {
41677         
41678         
41679         
41680         if(!this.activated){
41681             return;
41682         }
41683         /*
41684         if(Roo.isIE){
41685             this.win.focus();
41686             var r = this.doc.selection.createRange();
41687             if(r){
41688                 r.collapse(true);
41689                 r.pasteHTML(text);
41690                 this.syncValue();
41691                 this.deferFocus();
41692             
41693             }
41694             return;
41695         }
41696         */
41697         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41698             this.win.focus();
41699             
41700             
41701             // from jquery ui (MIT licenced)
41702             var range, node;
41703             var win = this.win;
41704             
41705             if (win.getSelection && win.getSelection().getRangeAt) {
41706                 range = win.getSelection().getRangeAt(0);
41707                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41708                 range.insertNode(node);
41709             } else if (win.document.selection && win.document.selection.createRange) {
41710                 // no firefox support
41711                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41712                 win.document.selection.createRange().pasteHTML(txt);
41713             } else {
41714                 // no firefox support
41715                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41716                 this.execCmd('InsertHTML', txt);
41717             } 
41718             
41719             this.syncValue();
41720             
41721             this.deferFocus();
41722         }
41723     },
41724  // private
41725     mozKeyPress : function(e){
41726         if(e.ctrlKey){
41727             var c = e.getCharCode(), cmd;
41728           
41729             if(c > 0){
41730                 c = String.fromCharCode(c).toLowerCase();
41731                 switch(c){
41732                     case 'b':
41733                         cmd = 'bold';
41734                         break;
41735                     case 'i':
41736                         cmd = 'italic';
41737                         break;
41738                     
41739                     case 'u':
41740                         cmd = 'underline';
41741                         break;
41742                     
41743                     case 'v':
41744                         this.cleanUpPaste.defer(100, this);
41745                         return;
41746                         
41747                 }
41748                 if(cmd){
41749                     this.win.focus();
41750                     this.execCmd(cmd);
41751                     this.deferFocus();
41752                     e.preventDefault();
41753                 }
41754                 
41755             }
41756         }
41757     },
41758
41759     // private
41760     fixKeys : function(){ // load time branching for fastest keydown performance
41761         if(Roo.isIE){
41762             return function(e){
41763                 var k = e.getKey(), r;
41764                 if(k == e.TAB){
41765                     e.stopEvent();
41766                     r = this.doc.selection.createRange();
41767                     if(r){
41768                         r.collapse(true);
41769                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41770                         this.deferFocus();
41771                     }
41772                     return;
41773                 }
41774                 
41775                 if(k == e.ENTER){
41776                     r = this.doc.selection.createRange();
41777                     if(r){
41778                         var target = r.parentElement();
41779                         if(!target || target.tagName.toLowerCase() != 'li'){
41780                             e.stopEvent();
41781                             r.pasteHTML('<br />');
41782                             r.collapse(false);
41783                             r.select();
41784                         }
41785                     }
41786                 }
41787                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41788                     this.cleanUpPaste.defer(100, this);
41789                     return;
41790                 }
41791                 
41792                 
41793             };
41794         }else if(Roo.isOpera){
41795             return function(e){
41796                 var k = e.getKey();
41797                 if(k == e.TAB){
41798                     e.stopEvent();
41799                     this.win.focus();
41800                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41801                     this.deferFocus();
41802                 }
41803                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41804                     this.cleanUpPaste.defer(100, this);
41805                     return;
41806                 }
41807                 
41808             };
41809         }else if(Roo.isSafari){
41810             return function(e){
41811                 var k = e.getKey();
41812                 
41813                 if(k == e.TAB){
41814                     e.stopEvent();
41815                     this.execCmd('InsertText','\t');
41816                     this.deferFocus();
41817                     return;
41818                 }
41819                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41820                     this.cleanUpPaste.defer(100, this);
41821                     return;
41822                 }
41823                 
41824              };
41825         }
41826     }(),
41827     
41828     getAllAncestors: function()
41829     {
41830         var p = this.getSelectedNode();
41831         var a = [];
41832         if (!p) {
41833             a.push(p); // push blank onto stack..
41834             p = this.getParentElement();
41835         }
41836         
41837         
41838         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41839             a.push(p);
41840             p = p.parentNode;
41841         }
41842         a.push(this.doc.body);
41843         return a;
41844     },
41845     lastSel : false,
41846     lastSelNode : false,
41847     
41848     
41849     getSelection : function() 
41850     {
41851         this.assignDocWin();
41852         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41853     },
41854     
41855     getSelectedNode: function() 
41856     {
41857         // this may only work on Gecko!!!
41858         
41859         // should we cache this!!!!
41860         
41861         
41862         
41863          
41864         var range = this.createRange(this.getSelection()).cloneRange();
41865         
41866         if (Roo.isIE) {
41867             var parent = range.parentElement();
41868             while (true) {
41869                 var testRange = range.duplicate();
41870                 testRange.moveToElementText(parent);
41871                 if (testRange.inRange(range)) {
41872                     break;
41873                 }
41874                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41875                     break;
41876                 }
41877                 parent = parent.parentElement;
41878             }
41879             return parent;
41880         }
41881         
41882         // is ancestor a text element.
41883         var ac =  range.commonAncestorContainer;
41884         if (ac.nodeType == 3) {
41885             ac = ac.parentNode;
41886         }
41887         
41888         var ar = ac.childNodes;
41889          
41890         var nodes = [];
41891         var other_nodes = [];
41892         var has_other_nodes = false;
41893         for (var i=0;i<ar.length;i++) {
41894             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41895                 continue;
41896             }
41897             // fullly contained node.
41898             
41899             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41900                 nodes.push(ar[i]);
41901                 continue;
41902             }
41903             
41904             // probably selected..
41905             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41906                 other_nodes.push(ar[i]);
41907                 continue;
41908             }
41909             // outer..
41910             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41911                 continue;
41912             }
41913             
41914             
41915             has_other_nodes = true;
41916         }
41917         if (!nodes.length && other_nodes.length) {
41918             nodes= other_nodes;
41919         }
41920         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41921             return false;
41922         }
41923         
41924         return nodes[0];
41925     },
41926     createRange: function(sel)
41927     {
41928         // this has strange effects when using with 
41929         // top toolbar - not sure if it's a great idea.
41930         //this.editor.contentWindow.focus();
41931         if (typeof sel != "undefined") {
41932             try {
41933                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41934             } catch(e) {
41935                 return this.doc.createRange();
41936             }
41937         } else {
41938             return this.doc.createRange();
41939         }
41940     },
41941     getParentElement: function()
41942     {
41943         
41944         this.assignDocWin();
41945         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41946         
41947         var range = this.createRange(sel);
41948          
41949         try {
41950             var p = range.commonAncestorContainer;
41951             while (p.nodeType == 3) { // text node
41952                 p = p.parentNode;
41953             }
41954             return p;
41955         } catch (e) {
41956             return null;
41957         }
41958     
41959     },
41960     /***
41961      *
41962      * Range intersection.. the hard stuff...
41963      *  '-1' = before
41964      *  '0' = hits..
41965      *  '1' = after.
41966      *         [ -- selected range --- ]
41967      *   [fail]                        [fail]
41968      *
41969      *    basically..
41970      *      if end is before start or  hits it. fail.
41971      *      if start is after end or hits it fail.
41972      *
41973      *   if either hits (but other is outside. - then it's not 
41974      *   
41975      *    
41976      **/
41977     
41978     
41979     // @see http://www.thismuchiknow.co.uk/?p=64.
41980     rangeIntersectsNode : function(range, node)
41981     {
41982         var nodeRange = node.ownerDocument.createRange();
41983         try {
41984             nodeRange.selectNode(node);
41985         } catch (e) {
41986             nodeRange.selectNodeContents(node);
41987         }
41988     
41989         var rangeStartRange = range.cloneRange();
41990         rangeStartRange.collapse(true);
41991     
41992         var rangeEndRange = range.cloneRange();
41993         rangeEndRange.collapse(false);
41994     
41995         var nodeStartRange = nodeRange.cloneRange();
41996         nodeStartRange.collapse(true);
41997     
41998         var nodeEndRange = nodeRange.cloneRange();
41999         nodeEndRange.collapse(false);
42000     
42001         return rangeStartRange.compareBoundaryPoints(
42002                  Range.START_TO_START, nodeEndRange) == -1 &&
42003                rangeEndRange.compareBoundaryPoints(
42004                  Range.START_TO_START, nodeStartRange) == 1;
42005         
42006          
42007     },
42008     rangeCompareNode : function(range, node)
42009     {
42010         var nodeRange = node.ownerDocument.createRange();
42011         try {
42012             nodeRange.selectNode(node);
42013         } catch (e) {
42014             nodeRange.selectNodeContents(node);
42015         }
42016         
42017         
42018         range.collapse(true);
42019     
42020         nodeRange.collapse(true);
42021      
42022         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42023         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42024          
42025         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42026         
42027         var nodeIsBefore   =  ss == 1;
42028         var nodeIsAfter    = ee == -1;
42029         
42030         if (nodeIsBefore && nodeIsAfter)
42031             return 0; // outer
42032         if (!nodeIsBefore && nodeIsAfter)
42033             return 1; //right trailed.
42034         
42035         if (nodeIsBefore && !nodeIsAfter)
42036             return 2;  // left trailed.
42037         // fully contined.
42038         return 3;
42039     },
42040
42041     // private? - in a new class?
42042     cleanUpPaste :  function()
42043     {
42044         // cleans up the whole document..
42045         Roo.log('cleanuppaste');
42046         
42047         this.cleanUpChildren(this.doc.body);
42048         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42049         if (clean != this.doc.body.innerHTML) {
42050             this.doc.body.innerHTML = clean;
42051         }
42052         
42053     },
42054     
42055     cleanWordChars : function(input) {// change the chars to hex code
42056         var he = Roo.HtmlEditorCore;
42057         
42058         var output = input;
42059         Roo.each(he.swapCodes, function(sw) { 
42060             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42061             
42062             output = output.replace(swapper, sw[1]);
42063         });
42064         
42065         return output;
42066     },
42067     
42068     
42069     cleanUpChildren : function (n)
42070     {
42071         if (!n.childNodes.length) {
42072             return;
42073         }
42074         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42075            this.cleanUpChild(n.childNodes[i]);
42076         }
42077     },
42078     
42079     
42080         
42081     
42082     cleanUpChild : function (node)
42083     {
42084         var ed = this;
42085         //console.log(node);
42086         if (node.nodeName == "#text") {
42087             // clean up silly Windows -- stuff?
42088             return; 
42089         }
42090         if (node.nodeName == "#comment") {
42091             node.parentNode.removeChild(node);
42092             // clean up silly Windows -- stuff?
42093             return; 
42094         }
42095         var lcname = node.tagName.toLowerCase();
42096         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42097         // whitelist of tags..
42098         
42099         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42100             // remove node.
42101             node.parentNode.removeChild(node);
42102             return;
42103             
42104         }
42105         
42106         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42107         
42108         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42109         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42110         
42111         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42112         //    remove_keep_children = true;
42113         //}
42114         
42115         if (remove_keep_children) {
42116             this.cleanUpChildren(node);
42117             // inserts everything just before this node...
42118             while (node.childNodes.length) {
42119                 var cn = node.childNodes[0];
42120                 node.removeChild(cn);
42121                 node.parentNode.insertBefore(cn, node);
42122             }
42123             node.parentNode.removeChild(node);
42124             return;
42125         }
42126         
42127         if (!node.attributes || !node.attributes.length) {
42128             this.cleanUpChildren(node);
42129             return;
42130         }
42131         
42132         function cleanAttr(n,v)
42133         {
42134             
42135             if (v.match(/^\./) || v.match(/^\//)) {
42136                 return;
42137             }
42138             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42139                 return;
42140             }
42141             if (v.match(/^#/)) {
42142                 return;
42143             }
42144 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42145             node.removeAttribute(n);
42146             
42147         }
42148         
42149         var cwhite = this.cwhite;
42150         var cblack = this.cblack;
42151             
42152         function cleanStyle(n,v)
42153         {
42154             if (v.match(/expression/)) { //XSS?? should we even bother..
42155                 node.removeAttribute(n);
42156                 return;
42157             }
42158             
42159             var parts = v.split(/;/);
42160             var clean = [];
42161             
42162             Roo.each(parts, function(p) {
42163                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42164                 if (!p.length) {
42165                     return true;
42166                 }
42167                 var l = p.split(':').shift().replace(/\s+/g,'');
42168                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42169                 
42170                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42171 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42172                     //node.removeAttribute(n);
42173                     return true;
42174                 }
42175                 //Roo.log()
42176                 // only allow 'c whitelisted system attributes'
42177                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42178 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42179                     //node.removeAttribute(n);
42180                     return true;
42181                 }
42182                 
42183                 
42184                  
42185                 
42186                 clean.push(p);
42187                 return true;
42188             });
42189             if (clean.length) { 
42190                 node.setAttribute(n, clean.join(';'));
42191             } else {
42192                 node.removeAttribute(n);
42193             }
42194             
42195         }
42196         
42197         
42198         for (var i = node.attributes.length-1; i > -1 ; i--) {
42199             var a = node.attributes[i];
42200             //console.log(a);
42201             
42202             if (a.name.toLowerCase().substr(0,2)=='on')  {
42203                 node.removeAttribute(a.name);
42204                 continue;
42205             }
42206             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42207                 node.removeAttribute(a.name);
42208                 continue;
42209             }
42210             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42211                 cleanAttr(a.name,a.value); // fixme..
42212                 continue;
42213             }
42214             if (a.name == 'style') {
42215                 cleanStyle(a.name,a.value);
42216                 continue;
42217             }
42218             /// clean up MS crap..
42219             // tecnically this should be a list of valid class'es..
42220             
42221             
42222             if (a.name == 'class') {
42223                 if (a.value.match(/^Mso/)) {
42224                     node.className = '';
42225                 }
42226                 
42227                 if (a.value.match(/body/)) {
42228                     node.className = '';
42229                 }
42230                 continue;
42231             }
42232             
42233             // style cleanup!?
42234             // class cleanup?
42235             
42236         }
42237         
42238         
42239         this.cleanUpChildren(node);
42240         
42241         
42242     },
42243     /**
42244      * Clean up MS wordisms...
42245      */
42246     cleanWord : function(node)
42247     {
42248         var _t = this;
42249         var cleanWordChildren = function()
42250         {
42251             if (!node.childNodes.length) {
42252                 return;
42253             }
42254             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42255                _t.cleanWord(node.childNodes[i]);
42256             }
42257         }
42258         
42259         
42260         if (!node) {
42261             this.cleanWord(this.doc.body);
42262             return;
42263         }
42264         if (node.nodeName == "#text") {
42265             // clean up silly Windows -- stuff?
42266             return; 
42267         }
42268         if (node.nodeName == "#comment") {
42269             node.parentNode.removeChild(node);
42270             // clean up silly Windows -- stuff?
42271             return; 
42272         }
42273         
42274         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42275             node.parentNode.removeChild(node);
42276             return;
42277         }
42278         
42279         // remove - but keep children..
42280         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42281             while (node.childNodes.length) {
42282                 var cn = node.childNodes[0];
42283                 node.removeChild(cn);
42284                 node.parentNode.insertBefore(cn, node);
42285             }
42286             node.parentNode.removeChild(node);
42287             cleanWordChildren();
42288             return;
42289         }
42290         // clean styles
42291         if (node.className.length) {
42292             
42293             var cn = node.className.split(/\W+/);
42294             var cna = [];
42295             Roo.each(cn, function(cls) {
42296                 if (cls.match(/Mso[a-zA-Z]+/)) {
42297                     return;
42298                 }
42299                 cna.push(cls);
42300             });
42301             node.className = cna.length ? cna.join(' ') : '';
42302             if (!cna.length) {
42303                 node.removeAttribute("class");
42304             }
42305         }
42306         
42307         if (node.hasAttribute("lang")) {
42308             node.removeAttribute("lang");
42309         }
42310         
42311         if (node.hasAttribute("style")) {
42312             
42313             var styles = node.getAttribute("style").split(";");
42314             var nstyle = [];
42315             Roo.each(styles, function(s) {
42316                 if (!s.match(/:/)) {
42317                     return;
42318                 }
42319                 var kv = s.split(":");
42320                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42321                     return;
42322                 }
42323                 // what ever is left... we allow.
42324                 nstyle.push(s);
42325             });
42326             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42327             if (!nstyle.length) {
42328                 node.removeAttribute('style');
42329             }
42330         }
42331         
42332         cleanWordChildren();
42333         
42334         
42335     },
42336     domToHTML : function(currentElement, depth, nopadtext) {
42337         
42338         depth = depth || 0;
42339         nopadtext = nopadtext || false;
42340     
42341         if (!currentElement) {
42342             return this.domToHTML(this.doc.body);
42343         }
42344         
42345         //Roo.log(currentElement);
42346         var j;
42347         var allText = false;
42348         var nodeName = currentElement.nodeName;
42349         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42350         
42351         if  (nodeName == '#text') {
42352             
42353             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42354         }
42355         
42356         
42357         var ret = '';
42358         if (nodeName != 'BODY') {
42359              
42360             var i = 0;
42361             // Prints the node tagName, such as <A>, <IMG>, etc
42362             if (tagName) {
42363                 var attr = [];
42364                 for(i = 0; i < currentElement.attributes.length;i++) {
42365                     // quoting?
42366                     var aname = currentElement.attributes.item(i).name;
42367                     if (!currentElement.attributes.item(i).value.length) {
42368                         continue;
42369                     }
42370                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42371                 }
42372                 
42373                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42374             } 
42375             else {
42376                 
42377                 // eack
42378             }
42379         } else {
42380             tagName = false;
42381         }
42382         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42383             return ret;
42384         }
42385         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42386             nopadtext = true;
42387         }
42388         
42389         
42390         // Traverse the tree
42391         i = 0;
42392         var currentElementChild = currentElement.childNodes.item(i);
42393         var allText = true;
42394         var innerHTML  = '';
42395         lastnode = '';
42396         while (currentElementChild) {
42397             // Formatting code (indent the tree so it looks nice on the screen)
42398             var nopad = nopadtext;
42399             if (lastnode == 'SPAN') {
42400                 nopad  = true;
42401             }
42402             // text
42403             if  (currentElementChild.nodeName == '#text') {
42404                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42405                 toadd = nopadtext ? toadd : toadd.trim();
42406                 if (!nopad && toadd.length > 80) {
42407                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42408                 }
42409                 innerHTML  += toadd;
42410                 
42411                 i++;
42412                 currentElementChild = currentElement.childNodes.item(i);
42413                 lastNode = '';
42414                 continue;
42415             }
42416             allText = false;
42417             
42418             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42419                 
42420             // Recursively traverse the tree structure of the child node
42421             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42422             lastnode = currentElementChild.nodeName;
42423             i++;
42424             currentElementChild=currentElement.childNodes.item(i);
42425         }
42426         
42427         ret += innerHTML;
42428         
42429         if (!allText) {
42430                 // The remaining code is mostly for formatting the tree
42431             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42432         }
42433         
42434         
42435         if (tagName) {
42436             ret+= "</"+tagName+">";
42437         }
42438         return ret;
42439         
42440     },
42441         
42442     applyBlacklists : function()
42443     {
42444         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42445         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42446         
42447         this.white = [];
42448         this.black = [];
42449         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42450             if (b.indexOf(tag) > -1) {
42451                 return;
42452             }
42453             this.white.push(tag);
42454             
42455         }, this);
42456         
42457         Roo.each(w, function(tag) {
42458             if (b.indexOf(tag) > -1) {
42459                 return;
42460             }
42461             if (this.white.indexOf(tag) > -1) {
42462                 return;
42463             }
42464             this.white.push(tag);
42465             
42466         }, this);
42467         
42468         
42469         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42470             if (w.indexOf(tag) > -1) {
42471                 return;
42472             }
42473             this.black.push(tag);
42474             
42475         }, this);
42476         
42477         Roo.each(b, function(tag) {
42478             if (w.indexOf(tag) > -1) {
42479                 return;
42480             }
42481             if (this.black.indexOf(tag) > -1) {
42482                 return;
42483             }
42484             this.black.push(tag);
42485             
42486         }, this);
42487         
42488         
42489         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42490         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42491         
42492         this.cwhite = [];
42493         this.cblack = [];
42494         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42495             if (b.indexOf(tag) > -1) {
42496                 return;
42497             }
42498             this.cwhite.push(tag);
42499             
42500         }, this);
42501         
42502         Roo.each(w, function(tag) {
42503             if (b.indexOf(tag) > -1) {
42504                 return;
42505             }
42506             if (this.cwhite.indexOf(tag) > -1) {
42507                 return;
42508             }
42509             this.cwhite.push(tag);
42510             
42511         }, this);
42512         
42513         
42514         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42515             if (w.indexOf(tag) > -1) {
42516                 return;
42517             }
42518             this.cblack.push(tag);
42519             
42520         }, this);
42521         
42522         Roo.each(b, function(tag) {
42523             if (w.indexOf(tag) > -1) {
42524                 return;
42525             }
42526             if (this.cblack.indexOf(tag) > -1) {
42527                 return;
42528             }
42529             this.cblack.push(tag);
42530             
42531         }, this);
42532     },
42533     
42534     setStylesheets : function(stylesheets)
42535     {
42536         if(typeof(stylesheets) == 'string'){
42537             Roo.get(this.iframe.contentDocument.head).createChild({
42538                 tag : 'link',
42539                 rel : 'stylesheet',
42540                 type : 'text/css',
42541                 href : stylesheets
42542             });
42543             
42544             return;
42545         }
42546         var _this = this;
42547      
42548         Roo.each(stylesheets, function(s) {
42549             if(!s.length){
42550                 return;
42551             }
42552             
42553             Roo.get(_this.iframe.contentDocument.head).createChild({
42554                 tag : 'link',
42555                 rel : 'stylesheet',
42556                 type : 'text/css',
42557                 href : s
42558             });
42559         });
42560
42561         
42562     },
42563     
42564     removeStylesheets : function()
42565     {
42566         var _this = this;
42567         
42568         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42569             s.remove();
42570         });
42571     }
42572     
42573     // hide stuff that is not compatible
42574     /**
42575      * @event blur
42576      * @hide
42577      */
42578     /**
42579      * @event change
42580      * @hide
42581      */
42582     /**
42583      * @event focus
42584      * @hide
42585      */
42586     /**
42587      * @event specialkey
42588      * @hide
42589      */
42590     /**
42591      * @cfg {String} fieldClass @hide
42592      */
42593     /**
42594      * @cfg {String} focusClass @hide
42595      */
42596     /**
42597      * @cfg {String} autoCreate @hide
42598      */
42599     /**
42600      * @cfg {String} inputType @hide
42601      */
42602     /**
42603      * @cfg {String} invalidClass @hide
42604      */
42605     /**
42606      * @cfg {String} invalidText @hide
42607      */
42608     /**
42609      * @cfg {String} msgFx @hide
42610      */
42611     /**
42612      * @cfg {String} validateOnBlur @hide
42613      */
42614 });
42615
42616 Roo.HtmlEditorCore.white = [
42617         'area', 'br', 'img', 'input', 'hr', 'wbr',
42618         
42619        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42620        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42621        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42622        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42623        'table',   'ul',         'xmp', 
42624        
42625        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42626       'thead',   'tr', 
42627      
42628       'dir', 'menu', 'ol', 'ul', 'dl',
42629        
42630       'embed',  'object'
42631 ];
42632
42633
42634 Roo.HtmlEditorCore.black = [
42635     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42636         'applet', // 
42637         'base',   'basefont', 'bgsound', 'blink',  'body', 
42638         'frame',  'frameset', 'head',    'html',   'ilayer', 
42639         'iframe', 'layer',  'link',     'meta',    'object',   
42640         'script', 'style' ,'title',  'xml' // clean later..
42641 ];
42642 Roo.HtmlEditorCore.clean = [
42643     'script', 'style', 'title', 'xml'
42644 ];
42645 Roo.HtmlEditorCore.remove = [
42646     'font'
42647 ];
42648 // attributes..
42649
42650 Roo.HtmlEditorCore.ablack = [
42651     'on'
42652 ];
42653     
42654 Roo.HtmlEditorCore.aclean = [ 
42655     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42656 ];
42657
42658 // protocols..
42659 Roo.HtmlEditorCore.pwhite= [
42660         'http',  'https',  'mailto'
42661 ];
42662
42663 // white listed style attributes.
42664 Roo.HtmlEditorCore.cwhite= [
42665       //  'text-align', /// default is to allow most things..
42666       
42667          
42668 //        'font-size'//??
42669 ];
42670
42671 // black listed style attributes.
42672 Roo.HtmlEditorCore.cblack= [
42673       //  'font-size' -- this can be set by the project 
42674 ];
42675
42676
42677 Roo.HtmlEditorCore.swapCodes   =[ 
42678     [    8211, "--" ], 
42679     [    8212, "--" ], 
42680     [    8216,  "'" ],  
42681     [    8217, "'" ],  
42682     [    8220, '"' ],  
42683     [    8221, '"' ],  
42684     [    8226, "*" ],  
42685     [    8230, "..." ]
42686 ]; 
42687
42688     //<script type="text/javascript">
42689
42690 /*
42691  * Ext JS Library 1.1.1
42692  * Copyright(c) 2006-2007, Ext JS, LLC.
42693  * Licence LGPL
42694  * 
42695  */
42696  
42697  
42698 Roo.form.HtmlEditor = function(config){
42699     
42700     
42701     
42702     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42703     
42704     if (!this.toolbars) {
42705         this.toolbars = [];
42706     }
42707     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42708     
42709     
42710 };
42711
42712 /**
42713  * @class Roo.form.HtmlEditor
42714  * @extends Roo.form.Field
42715  * Provides a lightweight HTML Editor component.
42716  *
42717  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42718  * 
42719  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42720  * supported by this editor.</b><br/><br/>
42721  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42722  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42723  */
42724 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42725     /**
42726      * @cfg {Boolean} clearUp
42727      */
42728     clearUp : true,
42729       /**
42730      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42731      */
42732     toolbars : false,
42733    
42734      /**
42735      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42736      *                        Roo.resizable.
42737      */
42738     resizable : false,
42739      /**
42740      * @cfg {Number} height (in pixels)
42741      */   
42742     height: 300,
42743    /**
42744      * @cfg {Number} width (in pixels)
42745      */   
42746     width: 500,
42747     
42748     /**
42749      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42750      * 
42751      */
42752     stylesheets: false,
42753     
42754     
42755      /**
42756      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42757      * 
42758      */
42759     cblack: false,
42760     /**
42761      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42762      * 
42763      */
42764     cwhite: false,
42765     
42766      /**
42767      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42768      * 
42769      */
42770     black: false,
42771     /**
42772      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42773      * 
42774      */
42775     white: false,
42776     
42777     // id of frame..
42778     frameId: false,
42779     
42780     // private properties
42781     validationEvent : false,
42782     deferHeight: true,
42783     initialized : false,
42784     activated : false,
42785     
42786     onFocus : Roo.emptyFn,
42787     iframePad:3,
42788     hideMode:'offsets',
42789     
42790     actionMode : 'container', // defaults to hiding it...
42791     
42792     defaultAutoCreate : { // modified by initCompnoent..
42793         tag: "textarea",
42794         style:"width:500px;height:300px;",
42795         autocomplete: "new-password"
42796     },
42797
42798     // private
42799     initComponent : function(){
42800         this.addEvents({
42801             /**
42802              * @event initialize
42803              * Fires when the editor is fully initialized (including the iframe)
42804              * @param {HtmlEditor} this
42805              */
42806             initialize: true,
42807             /**
42808              * @event activate
42809              * Fires when the editor is first receives the focus. Any insertion must wait
42810              * until after this event.
42811              * @param {HtmlEditor} this
42812              */
42813             activate: true,
42814              /**
42815              * @event beforesync
42816              * Fires before the textarea is updated with content from the editor iframe. Return false
42817              * to cancel the sync.
42818              * @param {HtmlEditor} this
42819              * @param {String} html
42820              */
42821             beforesync: true,
42822              /**
42823              * @event beforepush
42824              * Fires before the iframe editor is updated with content from the textarea. Return false
42825              * to cancel the push.
42826              * @param {HtmlEditor} this
42827              * @param {String} html
42828              */
42829             beforepush: true,
42830              /**
42831              * @event sync
42832              * Fires when the textarea is updated with content from the editor iframe.
42833              * @param {HtmlEditor} this
42834              * @param {String} html
42835              */
42836             sync: true,
42837              /**
42838              * @event push
42839              * Fires when the iframe editor is updated with content from the textarea.
42840              * @param {HtmlEditor} this
42841              * @param {String} html
42842              */
42843             push: true,
42844              /**
42845              * @event editmodechange
42846              * Fires when the editor switches edit modes
42847              * @param {HtmlEditor} this
42848              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42849              */
42850             editmodechange: true,
42851             /**
42852              * @event editorevent
42853              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42854              * @param {HtmlEditor} this
42855              */
42856             editorevent: true,
42857             /**
42858              * @event firstfocus
42859              * Fires when on first focus - needed by toolbars..
42860              * @param {HtmlEditor} this
42861              */
42862             firstfocus: true,
42863             /**
42864              * @event autosave
42865              * Auto save the htmlEditor value as a file into Events
42866              * @param {HtmlEditor} this
42867              */
42868             autosave: true,
42869             /**
42870              * @event savedpreview
42871              * preview the saved version of htmlEditor
42872              * @param {HtmlEditor} this
42873              */
42874             savedpreview: true,
42875             
42876             /**
42877             * @event stylesheetsclick
42878             * Fires when press the Sytlesheets button
42879             * @param {Roo.HtmlEditorCore} this
42880             */
42881             stylesheetsclick: true
42882         });
42883         this.defaultAutoCreate =  {
42884             tag: "textarea",
42885             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42886             autocomplete: "new-password"
42887         };
42888     },
42889
42890     /**
42891      * Protected method that will not generally be called directly. It
42892      * is called when the editor creates its toolbar. Override this method if you need to
42893      * add custom toolbar buttons.
42894      * @param {HtmlEditor} editor
42895      */
42896     createToolbar : function(editor){
42897         Roo.log("create toolbars");
42898         if (!editor.toolbars || !editor.toolbars.length) {
42899             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42900         }
42901         
42902         for (var i =0 ; i < editor.toolbars.length;i++) {
42903             editor.toolbars[i] = Roo.factory(
42904                     typeof(editor.toolbars[i]) == 'string' ?
42905                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42906                 Roo.form.HtmlEditor);
42907             editor.toolbars[i].init(editor);
42908         }
42909          
42910         
42911     },
42912
42913      
42914     // private
42915     onRender : function(ct, position)
42916     {
42917         var _t = this;
42918         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42919         
42920         this.wrap = this.el.wrap({
42921             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42922         });
42923         
42924         this.editorcore.onRender(ct, position);
42925          
42926         if (this.resizable) {
42927             this.resizeEl = new Roo.Resizable(this.wrap, {
42928                 pinned : true,
42929                 wrap: true,
42930                 dynamic : true,
42931                 minHeight : this.height,
42932                 height: this.height,
42933                 handles : this.resizable,
42934                 width: this.width,
42935                 listeners : {
42936                     resize : function(r, w, h) {
42937                         _t.onResize(w,h); // -something
42938                     }
42939                 }
42940             });
42941             
42942         }
42943         this.createToolbar(this);
42944        
42945         
42946         if(!this.width){
42947             this.setSize(this.wrap.getSize());
42948         }
42949         if (this.resizeEl) {
42950             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42951             // should trigger onReize..
42952         }
42953         
42954         this.keyNav = new Roo.KeyNav(this.el, {
42955             
42956             "tab" : function(e){
42957                 e.preventDefault();
42958                 
42959                 var value = this.getValue();
42960                 
42961                 var start = this.el.dom.selectionStart;
42962                 var end = this.el.dom.selectionEnd;
42963                 
42964                 if(!e.shiftKey){
42965                     
42966                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
42967                     this.el.dom.setSelectionRange(end + 1, end + 1);
42968                     return;
42969                 }
42970                 
42971                 var f = value.substring(0, start).split("\t");
42972                 
42973                 if(f.pop().length != 0){
42974                     return;
42975                 }
42976                 
42977                 this.setValue(f.join("\t") + value.substring(end));
42978                 this.el.dom.setSelectionRange(start - 1, start - 1);
42979                 
42980             },
42981             
42982             "home" : function(e){
42983                 e.preventDefault();
42984                 
42985                 var curr = this.el.dom.selectionStart;
42986                 var lines = this.getValue().split("\n");
42987                 
42988                 if(!lines.length){
42989                     return;
42990                 }
42991                 
42992                 if(e.ctrlKey){
42993                     this.el.dom.setSelectionRange(0, 0);
42994                     return;
42995                 }
42996                 
42997                 var pos = 0;
42998                 
42999                 for (var i = 0; i < lines.length;i++) {
43000                     pos += lines[i].length;
43001                     
43002                     if(i != 0){
43003                         pos += 1;
43004                     }
43005                     
43006                     if(pos < curr){
43007                         continue;
43008                     }
43009                     
43010                     pos -= lines[i].length;
43011                     
43012                     break;
43013                 }
43014                 
43015                 if(!e.shiftKey){
43016                     this.el.dom.setSelectionRange(pos, pos);
43017                     return;
43018                 }
43019                 
43020                 this.el.dom.selectionStart = pos;
43021                 this.el.dom.selectionEnd = curr;
43022             },
43023             
43024             "end" : function(e){
43025                 e.preventDefault();
43026                 
43027                 var curr = this.el.dom.selectionStart;
43028                 var lines = this.getValue().split("\n");
43029                 
43030                 if(!lines.length){
43031                     return;
43032                 }
43033                 
43034                 if(e.ctrlKey){
43035                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43036                     return;
43037                 }
43038                 
43039                 var pos = 0;
43040                 
43041                 for (var i = 0; i < lines.length;i++) {
43042                     
43043                     pos += lines[i].length;
43044                     
43045                     if(i != 0){
43046                         pos += 1;
43047                     }
43048                     
43049                     if(pos < curr){
43050                         continue;
43051                     }
43052                     
43053                     break;
43054                 }
43055                 
43056                 if(!e.shiftKey){
43057                     this.el.dom.setSelectionRange(pos, pos);
43058                     return;
43059                 }
43060                 
43061                 this.el.dom.selectionStart = curr;
43062                 this.el.dom.selectionEnd = pos;
43063             },
43064
43065             scope : this,
43066
43067             doRelay : function(foo, bar, hname){
43068                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43069             },
43070
43071             forceKeyDown: true
43072         });
43073         
43074 //        if(this.autosave && this.w){
43075 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43076 //        }
43077     },
43078
43079     // private
43080     onResize : function(w, h)
43081     {
43082         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43083         var ew = false;
43084         var eh = false;
43085         
43086         if(this.el ){
43087             if(typeof w == 'number'){
43088                 var aw = w - this.wrap.getFrameWidth('lr');
43089                 this.el.setWidth(this.adjustWidth('textarea', aw));
43090                 ew = aw;
43091             }
43092             if(typeof h == 'number'){
43093                 var tbh = 0;
43094                 for (var i =0; i < this.toolbars.length;i++) {
43095                     // fixme - ask toolbars for heights?
43096                     tbh += this.toolbars[i].tb.el.getHeight();
43097                     if (this.toolbars[i].footer) {
43098                         tbh += this.toolbars[i].footer.el.getHeight();
43099                     }
43100                 }
43101                 
43102                 
43103                 
43104                 
43105                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43106                 ah -= 5; // knock a few pixes off for look..
43107 //                Roo.log(ah);
43108                 this.el.setHeight(this.adjustWidth('textarea', ah));
43109                 var eh = ah;
43110             }
43111         }
43112         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43113         this.editorcore.onResize(ew,eh);
43114         
43115     },
43116
43117     /**
43118      * Toggles the editor between standard and source edit mode.
43119      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43120      */
43121     toggleSourceEdit : function(sourceEditMode)
43122     {
43123         this.editorcore.toggleSourceEdit(sourceEditMode);
43124         
43125         if(this.editorcore.sourceEditMode){
43126             Roo.log('editor - showing textarea');
43127             
43128 //            Roo.log('in');
43129 //            Roo.log(this.syncValue());
43130             this.editorcore.syncValue();
43131             this.el.removeClass('x-hidden');
43132             this.el.dom.removeAttribute('tabIndex');
43133             this.el.focus();
43134             
43135             for (var i = 0; i < this.toolbars.length; i++) {
43136                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43137                     this.toolbars[i].tb.hide();
43138                     this.toolbars[i].footer.hide();
43139                 }
43140             }
43141             
43142         }else{
43143             Roo.log('editor - hiding textarea');
43144 //            Roo.log('out')
43145 //            Roo.log(this.pushValue()); 
43146             this.editorcore.pushValue();
43147             
43148             this.el.addClass('x-hidden');
43149             this.el.dom.setAttribute('tabIndex', -1);
43150             
43151             for (var i = 0; i < this.toolbars.length; i++) {
43152                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43153                     this.toolbars[i].tb.show();
43154                     this.toolbars[i].footer.show();
43155                 }
43156             }
43157             
43158             //this.deferFocus();
43159         }
43160         
43161         this.setSize(this.wrap.getSize());
43162         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43163         
43164         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43165     },
43166  
43167     // private (for BoxComponent)
43168     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43169
43170     // private (for BoxComponent)
43171     getResizeEl : function(){
43172         return this.wrap;
43173     },
43174
43175     // private (for BoxComponent)
43176     getPositionEl : function(){
43177         return this.wrap;
43178     },
43179
43180     // private
43181     initEvents : function(){
43182         this.originalValue = this.getValue();
43183     },
43184
43185     /**
43186      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43187      * @method
43188      */
43189     markInvalid : Roo.emptyFn,
43190     /**
43191      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43192      * @method
43193      */
43194     clearInvalid : Roo.emptyFn,
43195
43196     setValue : function(v){
43197         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43198         this.editorcore.pushValue();
43199     },
43200
43201      
43202     // private
43203     deferFocus : function(){
43204         this.focus.defer(10, this);
43205     },
43206
43207     // doc'ed in Field
43208     focus : function(){
43209         this.editorcore.focus();
43210         
43211     },
43212       
43213
43214     // private
43215     onDestroy : function(){
43216         
43217         
43218         
43219         if(this.rendered){
43220             
43221             for (var i =0; i < this.toolbars.length;i++) {
43222                 // fixme - ask toolbars for heights?
43223                 this.toolbars[i].onDestroy();
43224             }
43225             
43226             this.wrap.dom.innerHTML = '';
43227             this.wrap.remove();
43228         }
43229     },
43230
43231     // private
43232     onFirstFocus : function(){
43233         //Roo.log("onFirstFocus");
43234         this.editorcore.onFirstFocus();
43235          for (var i =0; i < this.toolbars.length;i++) {
43236             this.toolbars[i].onFirstFocus();
43237         }
43238         
43239     },
43240     
43241     // private
43242     syncValue : function()
43243     {
43244         this.editorcore.syncValue();
43245     },
43246     
43247     pushValue : function()
43248     {
43249         this.editorcore.pushValue();
43250     },
43251     
43252     setStylesheets : function(stylesheets)
43253     {
43254         this.editorcore.setStylesheets(stylesheets);
43255     },
43256     
43257     removeStylesheets : function()
43258     {
43259         this.editorcore.removeStylesheets();
43260     }
43261      
43262     
43263     // hide stuff that is not compatible
43264     /**
43265      * @event blur
43266      * @hide
43267      */
43268     /**
43269      * @event change
43270      * @hide
43271      */
43272     /**
43273      * @event focus
43274      * @hide
43275      */
43276     /**
43277      * @event specialkey
43278      * @hide
43279      */
43280     /**
43281      * @cfg {String} fieldClass @hide
43282      */
43283     /**
43284      * @cfg {String} focusClass @hide
43285      */
43286     /**
43287      * @cfg {String} autoCreate @hide
43288      */
43289     /**
43290      * @cfg {String} inputType @hide
43291      */
43292     /**
43293      * @cfg {String} invalidClass @hide
43294      */
43295     /**
43296      * @cfg {String} invalidText @hide
43297      */
43298     /**
43299      * @cfg {String} msgFx @hide
43300      */
43301     /**
43302      * @cfg {String} validateOnBlur @hide
43303      */
43304 });
43305  
43306     // <script type="text/javascript">
43307 /*
43308  * Based on
43309  * Ext JS Library 1.1.1
43310  * Copyright(c) 2006-2007, Ext JS, LLC.
43311  *  
43312  
43313  */
43314
43315 /**
43316  * @class Roo.form.HtmlEditorToolbar1
43317  * Basic Toolbar
43318  * 
43319  * Usage:
43320  *
43321  new Roo.form.HtmlEditor({
43322     ....
43323     toolbars : [
43324         new Roo.form.HtmlEditorToolbar1({
43325             disable : { fonts: 1 , format: 1, ..., ... , ...],
43326             btns : [ .... ]
43327         })
43328     }
43329      
43330  * 
43331  * @cfg {Object} disable List of elements to disable..
43332  * @cfg {Array} btns List of additional buttons.
43333  * 
43334  * 
43335  * NEEDS Extra CSS? 
43336  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43337  */
43338  
43339 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43340 {
43341     
43342     Roo.apply(this, config);
43343     
43344     // default disabled, based on 'good practice'..
43345     this.disable = this.disable || {};
43346     Roo.applyIf(this.disable, {
43347         fontSize : true,
43348         colors : true,
43349         specialElements : true
43350     });
43351     
43352     
43353     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43354     // dont call parent... till later.
43355 }
43356
43357 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43358     
43359     tb: false,
43360     
43361     rendered: false,
43362     
43363     editor : false,
43364     editorcore : false,
43365     /**
43366      * @cfg {Object} disable  List of toolbar elements to disable
43367          
43368      */
43369     disable : false,
43370     
43371     
43372      /**
43373      * @cfg {String} createLinkText The default text for the create link prompt
43374      */
43375     createLinkText : 'Please enter the URL for the link:',
43376     /**
43377      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43378      */
43379     defaultLinkValue : 'http:/'+'/',
43380    
43381     
43382       /**
43383      * @cfg {Array} fontFamilies An array of available font families
43384      */
43385     fontFamilies : [
43386         'Arial',
43387         'Courier New',
43388         'Tahoma',
43389         'Times New Roman',
43390         'Verdana'
43391     ],
43392     
43393     specialChars : [
43394            "&#169;",
43395           "&#174;",     
43396           "&#8482;",    
43397           "&#163;" ,    
43398          // "&#8212;",    
43399           "&#8230;",    
43400           "&#247;" ,    
43401         //  "&#225;" ,     ?? a acute?
43402            "&#8364;"    , //Euro
43403        //   "&#8220;"    ,
43404         //  "&#8221;"    ,
43405         //  "&#8226;"    ,
43406           "&#176;"  //   , // degrees
43407
43408          // "&#233;"     , // e ecute
43409          // "&#250;"     , // u ecute?
43410     ],
43411     
43412     specialElements : [
43413         {
43414             text: "Insert Table",
43415             xtype: 'MenuItem',
43416             xns : Roo.Menu,
43417             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43418                 
43419         },
43420         {    
43421             text: "Insert Image",
43422             xtype: 'MenuItem',
43423             xns : Roo.Menu,
43424             ihtml : '<img src="about:blank"/>'
43425             
43426         }
43427         
43428          
43429     ],
43430     
43431     
43432     inputElements : [ 
43433             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43434             "input:submit", "input:button", "select", "textarea", "label" ],
43435     formats : [
43436         ["p"] ,  
43437         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43438         ["pre"],[ "code"], 
43439         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43440         ['div'],['span']
43441     ],
43442     
43443     cleanStyles : [
43444         "font-size"
43445     ],
43446      /**
43447      * @cfg {String} defaultFont default font to use.
43448      */
43449     defaultFont: 'tahoma',
43450    
43451     fontSelect : false,
43452     
43453     
43454     formatCombo : false,
43455     
43456     init : function(editor)
43457     {
43458         this.editor = editor;
43459         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43460         var editorcore = this.editorcore;
43461         
43462         var _t = this;
43463         
43464         var fid = editorcore.frameId;
43465         var etb = this;
43466         function btn(id, toggle, handler){
43467             var xid = fid + '-'+ id ;
43468             return {
43469                 id : xid,
43470                 cmd : id,
43471                 cls : 'x-btn-icon x-edit-'+id,
43472                 enableToggle:toggle !== false,
43473                 scope: _t, // was editor...
43474                 handler:handler||_t.relayBtnCmd,
43475                 clickEvent:'mousedown',
43476                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43477                 tabIndex:-1
43478             };
43479         }
43480         
43481         
43482         
43483         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43484         this.tb = tb;
43485          // stop form submits
43486         tb.el.on('click', function(e){
43487             e.preventDefault(); // what does this do?
43488         });
43489
43490         if(!this.disable.font) { // && !Roo.isSafari){
43491             /* why no safari for fonts 
43492             editor.fontSelect = tb.el.createChild({
43493                 tag:'select',
43494                 tabIndex: -1,
43495                 cls:'x-font-select',
43496                 html: this.createFontOptions()
43497             });
43498             
43499             editor.fontSelect.on('change', function(){
43500                 var font = editor.fontSelect.dom.value;
43501                 editor.relayCmd('fontname', font);
43502                 editor.deferFocus();
43503             }, editor);
43504             
43505             tb.add(
43506                 editor.fontSelect.dom,
43507                 '-'
43508             );
43509             */
43510             
43511         };
43512         if(!this.disable.formats){
43513             this.formatCombo = new Roo.form.ComboBox({
43514                 store: new Roo.data.SimpleStore({
43515                     id : 'tag',
43516                     fields: ['tag'],
43517                     data : this.formats // from states.js
43518                 }),
43519                 blockFocus : true,
43520                 name : '',
43521                 //autoCreate : {tag: "div",  size: "20"},
43522                 displayField:'tag',
43523                 typeAhead: false,
43524                 mode: 'local',
43525                 editable : false,
43526                 triggerAction: 'all',
43527                 emptyText:'Add tag',
43528                 selectOnFocus:true,
43529                 width:135,
43530                 listeners : {
43531                     'select': function(c, r, i) {
43532                         editorcore.insertTag(r.get('tag'));
43533                         editor.focus();
43534                     }
43535                 }
43536
43537             });
43538             tb.addField(this.formatCombo);
43539             
43540         }
43541         
43542         if(!this.disable.format){
43543             tb.add(
43544                 btn('bold'),
43545                 btn('italic'),
43546                 btn('underline')
43547             );
43548         };
43549         if(!this.disable.fontSize){
43550             tb.add(
43551                 '-',
43552                 
43553                 
43554                 btn('increasefontsize', false, editorcore.adjustFont),
43555                 btn('decreasefontsize', false, editorcore.adjustFont)
43556             );
43557         };
43558         
43559         
43560         if(!this.disable.colors){
43561             tb.add(
43562                 '-', {
43563                     id:editorcore.frameId +'-forecolor',
43564                     cls:'x-btn-icon x-edit-forecolor',
43565                     clickEvent:'mousedown',
43566                     tooltip: this.buttonTips['forecolor'] || undefined,
43567                     tabIndex:-1,
43568                     menu : new Roo.menu.ColorMenu({
43569                         allowReselect: true,
43570                         focus: Roo.emptyFn,
43571                         value:'000000',
43572                         plain:true,
43573                         selectHandler: function(cp, color){
43574                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43575                             editor.deferFocus();
43576                         },
43577                         scope: editorcore,
43578                         clickEvent:'mousedown'
43579                     })
43580                 }, {
43581                     id:editorcore.frameId +'backcolor',
43582                     cls:'x-btn-icon x-edit-backcolor',
43583                     clickEvent:'mousedown',
43584                     tooltip: this.buttonTips['backcolor'] || undefined,
43585                     tabIndex:-1,
43586                     menu : new Roo.menu.ColorMenu({
43587                         focus: Roo.emptyFn,
43588                         value:'FFFFFF',
43589                         plain:true,
43590                         allowReselect: true,
43591                         selectHandler: function(cp, color){
43592                             if(Roo.isGecko){
43593                                 editorcore.execCmd('useCSS', false);
43594                                 editorcore.execCmd('hilitecolor', color);
43595                                 editorcore.execCmd('useCSS', true);
43596                                 editor.deferFocus();
43597                             }else{
43598                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43599                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43600                                 editor.deferFocus();
43601                             }
43602                         },
43603                         scope:editorcore,
43604                         clickEvent:'mousedown'
43605                     })
43606                 }
43607             );
43608         };
43609         // now add all the items...
43610         
43611
43612         if(!this.disable.alignments){
43613             tb.add(
43614                 '-',
43615                 btn('justifyleft'),
43616                 btn('justifycenter'),
43617                 btn('justifyright')
43618             );
43619         };
43620
43621         //if(!Roo.isSafari){
43622             if(!this.disable.links){
43623                 tb.add(
43624                     '-',
43625                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43626                 );
43627             };
43628
43629             if(!this.disable.lists){
43630                 tb.add(
43631                     '-',
43632                     btn('insertorderedlist'),
43633                     btn('insertunorderedlist')
43634                 );
43635             }
43636             if(!this.disable.sourceEdit){
43637                 tb.add(
43638                     '-',
43639                     btn('sourceedit', true, function(btn){
43640                         this.toggleSourceEdit(btn.pressed);
43641                     })
43642                 );
43643             }
43644         //}
43645         
43646         var smenu = { };
43647         // special menu.. - needs to be tidied up..
43648         if (!this.disable.special) {
43649             smenu = {
43650                 text: "&#169;",
43651                 cls: 'x-edit-none',
43652                 
43653                 menu : {
43654                     items : []
43655                 }
43656             };
43657             for (var i =0; i < this.specialChars.length; i++) {
43658                 smenu.menu.items.push({
43659                     
43660                     html: this.specialChars[i],
43661                     handler: function(a,b) {
43662                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43663                         //editor.insertAtCursor(a.html);
43664                         
43665                     },
43666                     tabIndex:-1
43667                 });
43668             }
43669             
43670             
43671             tb.add(smenu);
43672             
43673             
43674         }
43675         
43676         var cmenu = { };
43677         if (!this.disable.cleanStyles) {
43678             cmenu = {
43679                 cls: 'x-btn-icon x-btn-clear',
43680                 
43681                 menu : {
43682                     items : []
43683                 }
43684             };
43685             for (var i =0; i < this.cleanStyles.length; i++) {
43686                 cmenu.menu.items.push({
43687                     actiontype : this.cleanStyles[i],
43688                     html: 'Remove ' + this.cleanStyles[i],
43689                     handler: function(a,b) {
43690 //                        Roo.log(a);
43691 //                        Roo.log(b);
43692                         var c = Roo.get(editorcore.doc.body);
43693                         c.select('[style]').each(function(s) {
43694                             s.dom.style.removeProperty(a.actiontype);
43695                         });
43696                         editorcore.syncValue();
43697                     },
43698                     tabIndex:-1
43699                 });
43700             }
43701             cmenu.menu.items.push({
43702                 actiontype : 'word',
43703                 html: 'Remove MS Word Formating',
43704                 handler: function(a,b) {
43705                     editorcore.cleanWord();
43706                     editorcore.syncValue();
43707                 },
43708                 tabIndex:-1
43709             });
43710             
43711             cmenu.menu.items.push({
43712                 actiontype : 'all',
43713                 html: 'Remove All Styles',
43714                 handler: function(a,b) {
43715                     
43716                     var c = Roo.get(editorcore.doc.body);
43717                     c.select('[style]').each(function(s) {
43718                         s.dom.removeAttribute('style');
43719                     });
43720                     editorcore.syncValue();
43721                 },
43722                 tabIndex:-1
43723             });
43724              cmenu.menu.items.push({
43725                 actiontype : 'word',
43726                 html: 'Tidy HTML Source',
43727                 handler: function(a,b) {
43728                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43729                     editorcore.syncValue();
43730                 },
43731                 tabIndex:-1
43732             });
43733             
43734             
43735             tb.add(cmenu);
43736         }
43737          
43738         if (!this.disable.specialElements) {
43739             var semenu = {
43740                 text: "Other;",
43741                 cls: 'x-edit-none',
43742                 menu : {
43743                     items : []
43744                 }
43745             };
43746             for (var i =0; i < this.specialElements.length; i++) {
43747                 semenu.menu.items.push(
43748                     Roo.apply({ 
43749                         handler: function(a,b) {
43750                             editor.insertAtCursor(this.ihtml);
43751                         }
43752                     }, this.specialElements[i])
43753                 );
43754                     
43755             }
43756             
43757             tb.add(semenu);
43758             
43759             
43760         }
43761          
43762         
43763         if (this.btns) {
43764             for(var i =0; i< this.btns.length;i++) {
43765                 var b = Roo.factory(this.btns[i],Roo.form);
43766                 b.cls =  'x-edit-none';
43767                 
43768                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43769                     b.cls += ' x-init-enable';
43770                 }
43771                 
43772                 b.scope = editorcore;
43773                 tb.add(b);
43774             }
43775         
43776         }
43777         
43778         
43779         
43780         // disable everything...
43781         
43782         this.tb.items.each(function(item){
43783             
43784            if(
43785                 item.id != editorcore.frameId+ '-sourceedit' && 
43786                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43787             ){
43788                 
43789                 item.disable();
43790             }
43791         });
43792         this.rendered = true;
43793         
43794         // the all the btns;
43795         editor.on('editorevent', this.updateToolbar, this);
43796         // other toolbars need to implement this..
43797         //editor.on('editmodechange', this.updateToolbar, this);
43798     },
43799     
43800     
43801     relayBtnCmd : function(btn) {
43802         this.editorcore.relayCmd(btn.cmd);
43803     },
43804     // private used internally
43805     createLink : function(){
43806         Roo.log("create link?");
43807         var url = prompt(this.createLinkText, this.defaultLinkValue);
43808         if(url && url != 'http:/'+'/'){
43809             this.editorcore.relayCmd('createlink', url);
43810         }
43811     },
43812
43813     
43814     /**
43815      * Protected method that will not generally be called directly. It triggers
43816      * a toolbar update by reading the markup state of the current selection in the editor.
43817      */
43818     updateToolbar: function(){
43819
43820         if(!this.editorcore.activated){
43821             this.editor.onFirstFocus();
43822             return;
43823         }
43824
43825         var btns = this.tb.items.map, 
43826             doc = this.editorcore.doc,
43827             frameId = this.editorcore.frameId;
43828
43829         if(!this.disable.font && !Roo.isSafari){
43830             /*
43831             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43832             if(name != this.fontSelect.dom.value){
43833                 this.fontSelect.dom.value = name;
43834             }
43835             */
43836         }
43837         if(!this.disable.format){
43838             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43839             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43840             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43841         }
43842         if(!this.disable.alignments){
43843             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43844             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43845             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43846         }
43847         if(!Roo.isSafari && !this.disable.lists){
43848             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43849             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43850         }
43851         
43852         var ans = this.editorcore.getAllAncestors();
43853         if (this.formatCombo) {
43854             
43855             
43856             var store = this.formatCombo.store;
43857             this.formatCombo.setValue("");
43858             for (var i =0; i < ans.length;i++) {
43859                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43860                     // select it..
43861                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43862                     break;
43863                 }
43864             }
43865         }
43866         
43867         
43868         
43869         // hides menus... - so this cant be on a menu...
43870         Roo.menu.MenuMgr.hideAll();
43871
43872         //this.editorsyncValue();
43873     },
43874    
43875     
43876     createFontOptions : function(){
43877         var buf = [], fs = this.fontFamilies, ff, lc;
43878         
43879         
43880         
43881         for(var i = 0, len = fs.length; i< len; i++){
43882             ff = fs[i];
43883             lc = ff.toLowerCase();
43884             buf.push(
43885                 '<option value="',lc,'" style="font-family:',ff,';"',
43886                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43887                     ff,
43888                 '</option>'
43889             );
43890         }
43891         return buf.join('');
43892     },
43893     
43894     toggleSourceEdit : function(sourceEditMode){
43895         
43896         Roo.log("toolbar toogle");
43897         if(sourceEditMode === undefined){
43898             sourceEditMode = !this.sourceEditMode;
43899         }
43900         this.sourceEditMode = sourceEditMode === true;
43901         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43902         // just toggle the button?
43903         if(btn.pressed !== this.sourceEditMode){
43904             btn.toggle(this.sourceEditMode);
43905             return;
43906         }
43907         
43908         if(sourceEditMode){
43909             Roo.log("disabling buttons");
43910             this.tb.items.each(function(item){
43911                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43912                     item.disable();
43913                 }
43914             });
43915           
43916         }else{
43917             Roo.log("enabling buttons");
43918             if(this.editorcore.initialized){
43919                 this.tb.items.each(function(item){
43920                     item.enable();
43921                 });
43922             }
43923             
43924         }
43925         Roo.log("calling toggole on editor");
43926         // tell the editor that it's been pressed..
43927         this.editor.toggleSourceEdit(sourceEditMode);
43928        
43929     },
43930      /**
43931      * Object collection of toolbar tooltips for the buttons in the editor. The key
43932      * is the command id associated with that button and the value is a valid QuickTips object.
43933      * For example:
43934 <pre><code>
43935 {
43936     bold : {
43937         title: 'Bold (Ctrl+B)',
43938         text: 'Make the selected text bold.',
43939         cls: 'x-html-editor-tip'
43940     },
43941     italic : {
43942         title: 'Italic (Ctrl+I)',
43943         text: 'Make the selected text italic.',
43944         cls: 'x-html-editor-tip'
43945     },
43946     ...
43947 </code></pre>
43948     * @type Object
43949      */
43950     buttonTips : {
43951         bold : {
43952             title: 'Bold (Ctrl+B)',
43953             text: 'Make the selected text bold.',
43954             cls: 'x-html-editor-tip'
43955         },
43956         italic : {
43957             title: 'Italic (Ctrl+I)',
43958             text: 'Make the selected text italic.',
43959             cls: 'x-html-editor-tip'
43960         },
43961         underline : {
43962             title: 'Underline (Ctrl+U)',
43963             text: 'Underline the selected text.',
43964             cls: 'x-html-editor-tip'
43965         },
43966         increasefontsize : {
43967             title: 'Grow Text',
43968             text: 'Increase the font size.',
43969             cls: 'x-html-editor-tip'
43970         },
43971         decreasefontsize : {
43972             title: 'Shrink Text',
43973             text: 'Decrease the font size.',
43974             cls: 'x-html-editor-tip'
43975         },
43976         backcolor : {
43977             title: 'Text Highlight Color',
43978             text: 'Change the background color of the selected text.',
43979             cls: 'x-html-editor-tip'
43980         },
43981         forecolor : {
43982             title: 'Font Color',
43983             text: 'Change the color of the selected text.',
43984             cls: 'x-html-editor-tip'
43985         },
43986         justifyleft : {
43987             title: 'Align Text Left',
43988             text: 'Align text to the left.',
43989             cls: 'x-html-editor-tip'
43990         },
43991         justifycenter : {
43992             title: 'Center Text',
43993             text: 'Center text in the editor.',
43994             cls: 'x-html-editor-tip'
43995         },
43996         justifyright : {
43997             title: 'Align Text Right',
43998             text: 'Align text to the right.',
43999             cls: 'x-html-editor-tip'
44000         },
44001         insertunorderedlist : {
44002             title: 'Bullet List',
44003             text: 'Start a bulleted list.',
44004             cls: 'x-html-editor-tip'
44005         },
44006         insertorderedlist : {
44007             title: 'Numbered List',
44008             text: 'Start a numbered list.',
44009             cls: 'x-html-editor-tip'
44010         },
44011         createlink : {
44012             title: 'Hyperlink',
44013             text: 'Make the selected text a hyperlink.',
44014             cls: 'x-html-editor-tip'
44015         },
44016         sourceedit : {
44017             title: 'Source Edit',
44018             text: 'Switch to source editing mode.',
44019             cls: 'x-html-editor-tip'
44020         }
44021     },
44022     // private
44023     onDestroy : function(){
44024         if(this.rendered){
44025             
44026             this.tb.items.each(function(item){
44027                 if(item.menu){
44028                     item.menu.removeAll();
44029                     if(item.menu.el){
44030                         item.menu.el.destroy();
44031                     }
44032                 }
44033                 item.destroy();
44034             });
44035              
44036         }
44037     },
44038     onFirstFocus: function() {
44039         this.tb.items.each(function(item){
44040            item.enable();
44041         });
44042     }
44043 });
44044
44045
44046
44047
44048 // <script type="text/javascript">
44049 /*
44050  * Based on
44051  * Ext JS Library 1.1.1
44052  * Copyright(c) 2006-2007, Ext JS, LLC.
44053  *  
44054  
44055  */
44056
44057  
44058 /**
44059  * @class Roo.form.HtmlEditor.ToolbarContext
44060  * Context Toolbar
44061  * 
44062  * Usage:
44063  *
44064  new Roo.form.HtmlEditor({
44065     ....
44066     toolbars : [
44067         { xtype: 'ToolbarStandard', styles : {} }
44068         { xtype: 'ToolbarContext', disable : {} }
44069     ]
44070 })
44071
44072      
44073  * 
44074  * @config : {Object} disable List of elements to disable.. (not done yet.)
44075  * @config : {Object} styles  Map of styles available.
44076  * 
44077  */
44078
44079 Roo.form.HtmlEditor.ToolbarContext = function(config)
44080 {
44081     
44082     Roo.apply(this, config);
44083     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44084     // dont call parent... till later.
44085     this.styles = this.styles || {};
44086 }
44087
44088  
44089
44090 Roo.form.HtmlEditor.ToolbarContext.types = {
44091     'IMG' : {
44092         width : {
44093             title: "Width",
44094             width: 40
44095         },
44096         height:  {
44097             title: "Height",
44098             width: 40
44099         },
44100         align: {
44101             title: "Align",
44102             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44103             width : 80
44104             
44105         },
44106         border: {
44107             title: "Border",
44108             width: 40
44109         },
44110         alt: {
44111             title: "Alt",
44112             width: 120
44113         },
44114         src : {
44115             title: "Src",
44116             width: 220
44117         }
44118         
44119     },
44120     'A' : {
44121         name : {
44122             title: "Name",
44123             width: 50
44124         },
44125         target:  {
44126             title: "Target",
44127             width: 120
44128         },
44129         href:  {
44130             title: "Href",
44131             width: 220
44132         } // border?
44133         
44134     },
44135     'TABLE' : {
44136         rows : {
44137             title: "Rows",
44138             width: 20
44139         },
44140         cols : {
44141             title: "Cols",
44142             width: 20
44143         },
44144         width : {
44145             title: "Width",
44146             width: 40
44147         },
44148         height : {
44149             title: "Height",
44150             width: 40
44151         },
44152         border : {
44153             title: "Border",
44154             width: 20
44155         }
44156     },
44157     'TD' : {
44158         width : {
44159             title: "Width",
44160             width: 40
44161         },
44162         height : {
44163             title: "Height",
44164             width: 40
44165         },   
44166         align: {
44167             title: "Align",
44168             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44169             width: 80
44170         },
44171         valign: {
44172             title: "Valign",
44173             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44174             width: 80
44175         },
44176         colspan: {
44177             title: "Colspan",
44178             width: 20
44179             
44180         },
44181          'font-family'  : {
44182             title : "Font",
44183             style : 'fontFamily',
44184             displayField: 'display',
44185             optname : 'font-family',
44186             width: 140
44187         }
44188     },
44189     'INPUT' : {
44190         name : {
44191             title: "name",
44192             width: 120
44193         },
44194         value : {
44195             title: "Value",
44196             width: 120
44197         },
44198         width : {
44199             title: "Width",
44200             width: 40
44201         }
44202     },
44203     'LABEL' : {
44204         'for' : {
44205             title: "For",
44206             width: 120
44207         }
44208     },
44209     'TEXTAREA' : {
44210           name : {
44211             title: "name",
44212             width: 120
44213         },
44214         rows : {
44215             title: "Rows",
44216             width: 20
44217         },
44218         cols : {
44219             title: "Cols",
44220             width: 20
44221         }
44222     },
44223     'SELECT' : {
44224         name : {
44225             title: "name",
44226             width: 120
44227         },
44228         selectoptions : {
44229             title: "Options",
44230             width: 200
44231         }
44232     },
44233     
44234     // should we really allow this??
44235     // should this just be 
44236     'BODY' : {
44237         title : {
44238             title: "Title",
44239             width: 200,
44240             disabled : true
44241         }
44242     },
44243     'SPAN' : {
44244         'font-family'  : {
44245             title : "Font",
44246             style : 'fontFamily',
44247             displayField: 'display',
44248             optname : 'font-family',
44249             width: 140
44250         }
44251     },
44252     'DIV' : {
44253         'font-family'  : {
44254             title : "Font",
44255             style : 'fontFamily',
44256             displayField: 'display',
44257             optname : 'font-family',
44258             width: 140
44259         }
44260     },
44261      'P' : {
44262         'font-family'  : {
44263             title : "Font",
44264             style : 'fontFamily',
44265             displayField: 'display',
44266             optname : 'font-family',
44267             width: 140
44268         }
44269     },
44270     
44271     '*' : {
44272         // empty..
44273     }
44274
44275 };
44276
44277 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44278 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44279
44280 Roo.form.HtmlEditor.ToolbarContext.options = {
44281         'font-family'  : [ 
44282                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44283                 [ 'Courier New', 'Courier New'],
44284                 [ 'Tahoma', 'Tahoma'],
44285                 [ 'Times New Roman,serif', 'Times'],
44286                 [ 'Verdana','Verdana' ]
44287         ]
44288 };
44289
44290 // fixme - these need to be configurable..
44291  
44292
44293 Roo.form.HtmlEditor.ToolbarContext.types
44294
44295
44296 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44297     
44298     tb: false,
44299     
44300     rendered: false,
44301     
44302     editor : false,
44303     editorcore : false,
44304     /**
44305      * @cfg {Object} disable  List of toolbar elements to disable
44306          
44307      */
44308     disable : false,
44309     /**
44310      * @cfg {Object} styles List of styles 
44311      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44312      *
44313      * These must be defined in the page, so they get rendered correctly..
44314      * .headline { }
44315      * TD.underline { }
44316      * 
44317      */
44318     styles : false,
44319     
44320     options: false,
44321     
44322     toolbars : false,
44323     
44324     init : function(editor)
44325     {
44326         this.editor = editor;
44327         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44328         var editorcore = this.editorcore;
44329         
44330         var fid = editorcore.frameId;
44331         var etb = this;
44332         function btn(id, toggle, handler){
44333             var xid = fid + '-'+ id ;
44334             return {
44335                 id : xid,
44336                 cmd : id,
44337                 cls : 'x-btn-icon x-edit-'+id,
44338                 enableToggle:toggle !== false,
44339                 scope: editorcore, // was editor...
44340                 handler:handler||editorcore.relayBtnCmd,
44341                 clickEvent:'mousedown',
44342                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44343                 tabIndex:-1
44344             };
44345         }
44346         // create a new element.
44347         var wdiv = editor.wrap.createChild({
44348                 tag: 'div'
44349             }, editor.wrap.dom.firstChild.nextSibling, true);
44350         
44351         // can we do this more than once??
44352         
44353          // stop form submits
44354       
44355  
44356         // disable everything...
44357         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44358         this.toolbars = {};
44359            
44360         for (var i in  ty) {
44361           
44362             this.toolbars[i] = this.buildToolbar(ty[i],i);
44363         }
44364         this.tb = this.toolbars.BODY;
44365         this.tb.el.show();
44366         this.buildFooter();
44367         this.footer.show();
44368         editor.on('hide', function( ) { this.footer.hide() }, this);
44369         editor.on('show', function( ) { this.footer.show() }, this);
44370         
44371          
44372         this.rendered = true;
44373         
44374         // the all the btns;
44375         editor.on('editorevent', this.updateToolbar, this);
44376         // other toolbars need to implement this..
44377         //editor.on('editmodechange', this.updateToolbar, this);
44378     },
44379     
44380     
44381     
44382     /**
44383      * Protected method that will not generally be called directly. It triggers
44384      * a toolbar update by reading the markup state of the current selection in the editor.
44385      */
44386     updateToolbar: function(editor,ev,sel){
44387
44388         //Roo.log(ev);
44389         // capture mouse up - this is handy for selecting images..
44390         // perhaps should go somewhere else...
44391         if(!this.editorcore.activated){
44392              this.editor.onFirstFocus();
44393             return;
44394         }
44395         
44396         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44397         // selectNode - might want to handle IE?
44398         if (ev &&
44399             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44400             ev.target && ev.target.tagName == 'IMG') {
44401             // they have click on an image...
44402             // let's see if we can change the selection...
44403             sel = ev.target;
44404          
44405               var nodeRange = sel.ownerDocument.createRange();
44406             try {
44407                 nodeRange.selectNode(sel);
44408             } catch (e) {
44409                 nodeRange.selectNodeContents(sel);
44410             }
44411             //nodeRange.collapse(true);
44412             var s = this.editorcore.win.getSelection();
44413             s.removeAllRanges();
44414             s.addRange(nodeRange);
44415         }  
44416         
44417       
44418         var updateFooter = sel ? false : true;
44419         
44420         
44421         var ans = this.editorcore.getAllAncestors();
44422         
44423         // pick
44424         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44425         
44426         if (!sel) { 
44427             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44428             sel = sel ? sel : this.editorcore.doc.body;
44429             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44430             
44431         }
44432         // pick a menu that exists..
44433         var tn = sel.tagName.toUpperCase();
44434         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44435         
44436         tn = sel.tagName.toUpperCase();
44437         
44438         var lastSel = this.tb.selectedNode
44439         
44440         this.tb.selectedNode = sel;
44441         
44442         // if current menu does not match..
44443         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44444                 
44445             this.tb.el.hide();
44446             ///console.log("show: " + tn);
44447             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44448             this.tb.el.show();
44449             // update name
44450             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44451             
44452             
44453             // update attributes
44454             if (this.tb.fields) {
44455                 this.tb.fields.each(function(e) {
44456                     if (e.stylename) {
44457                         e.setValue(sel.style[e.stylename]);
44458                         return;
44459                     } 
44460                    e.setValue(sel.getAttribute(e.attrname));
44461                 });
44462             }
44463             
44464             var hasStyles = false;
44465             for(var i in this.styles) {
44466                 hasStyles = true;
44467                 break;
44468             }
44469             
44470             // update styles
44471             if (hasStyles) { 
44472                 var st = this.tb.fields.item(0);
44473                 
44474                 st.store.removeAll();
44475                
44476                 
44477                 var cn = sel.className.split(/\s+/);
44478                 
44479                 var avs = [];
44480                 if (this.styles['*']) {
44481                     
44482                     Roo.each(this.styles['*'], function(v) {
44483                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44484                     });
44485                 }
44486                 if (this.styles[tn]) { 
44487                     Roo.each(this.styles[tn], function(v) {
44488                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44489                     });
44490                 }
44491                 
44492                 st.store.loadData(avs);
44493                 st.collapse();
44494                 st.setValue(cn);
44495             }
44496             // flag our selected Node.
44497             this.tb.selectedNode = sel;
44498            
44499            
44500             Roo.menu.MenuMgr.hideAll();
44501
44502         }
44503         
44504         if (!updateFooter) {
44505             //this.footDisp.dom.innerHTML = ''; 
44506             return;
44507         }
44508         // update the footer
44509         //
44510         var html = '';
44511         
44512         this.footerEls = ans.reverse();
44513         Roo.each(this.footerEls, function(a,i) {
44514             if (!a) { return; }
44515             html += html.length ? ' &gt; '  :  '';
44516             
44517             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44518             
44519         });
44520        
44521         // 
44522         var sz = this.footDisp.up('td').getSize();
44523         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44524         this.footDisp.dom.style.marginLeft = '5px';
44525         
44526         this.footDisp.dom.style.overflow = 'hidden';
44527         
44528         this.footDisp.dom.innerHTML = html;
44529             
44530         //this.editorsyncValue();
44531     },
44532      
44533     
44534    
44535        
44536     // private
44537     onDestroy : function(){
44538         if(this.rendered){
44539             
44540             this.tb.items.each(function(item){
44541                 if(item.menu){
44542                     item.menu.removeAll();
44543                     if(item.menu.el){
44544                         item.menu.el.destroy();
44545                     }
44546                 }
44547                 item.destroy();
44548             });
44549              
44550         }
44551     },
44552     onFirstFocus: function() {
44553         // need to do this for all the toolbars..
44554         this.tb.items.each(function(item){
44555            item.enable();
44556         });
44557     },
44558     buildToolbar: function(tlist, nm)
44559     {
44560         var editor = this.editor;
44561         var editorcore = this.editorcore;
44562          // create a new element.
44563         var wdiv = editor.wrap.createChild({
44564                 tag: 'div'
44565             }, editor.wrap.dom.firstChild.nextSibling, true);
44566         
44567        
44568         var tb = new Roo.Toolbar(wdiv);
44569         // add the name..
44570         
44571         tb.add(nm+ ":&nbsp;");
44572         
44573         var styles = [];
44574         for(var i in this.styles) {
44575             styles.push(i);
44576         }
44577         
44578         // styles...
44579         if (styles && styles.length) {
44580             
44581             // this needs a multi-select checkbox...
44582             tb.addField( new Roo.form.ComboBox({
44583                 store: new Roo.data.SimpleStore({
44584                     id : 'val',
44585                     fields: ['val', 'selected'],
44586                     data : [] 
44587                 }),
44588                 name : '-roo-edit-className',
44589                 attrname : 'className',
44590                 displayField: 'val',
44591                 typeAhead: false,
44592                 mode: 'local',
44593                 editable : false,
44594                 triggerAction: 'all',
44595                 emptyText:'Select Style',
44596                 selectOnFocus:true,
44597                 width: 130,
44598                 listeners : {
44599                     'select': function(c, r, i) {
44600                         // initial support only for on class per el..
44601                         tb.selectedNode.className =  r ? r.get('val') : '';
44602                         editorcore.syncValue();
44603                     }
44604                 }
44605     
44606             }));
44607         }
44608         
44609         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44610         var tbops = tbc.options;
44611         
44612         for (var i in tlist) {
44613             
44614             var item = tlist[i];
44615             tb.add(item.title + ":&nbsp;");
44616             
44617             
44618             //optname == used so you can configure the options available..
44619             var opts = item.opts ? item.opts : false;
44620             if (item.optname) {
44621                 opts = tbops[item.optname];
44622            
44623             }
44624             
44625             if (opts) {
44626                 // opts == pulldown..
44627                 tb.addField( new Roo.form.ComboBox({
44628                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44629                         id : 'val',
44630                         fields: ['val', 'display'],
44631                         data : opts  
44632                     }),
44633                     name : '-roo-edit-' + i,
44634                     attrname : i,
44635                     stylename : item.style ? item.style : false,
44636                     displayField: item.displayField ? item.displayField : 'val',
44637                     valueField :  'val',
44638                     typeAhead: false,
44639                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44640                     editable : false,
44641                     triggerAction: 'all',
44642                     emptyText:'Select',
44643                     selectOnFocus:true,
44644                     width: item.width ? item.width  : 130,
44645                     listeners : {
44646                         'select': function(c, r, i) {
44647                             if (c.stylename) {
44648                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44649                                 return;
44650                             }
44651                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44652                         }
44653                     }
44654
44655                 }));
44656                 continue;
44657                     
44658                  
44659                 
44660                 tb.addField( new Roo.form.TextField({
44661                     name: i,
44662                     width: 100,
44663                     //allowBlank:false,
44664                     value: ''
44665                 }));
44666                 continue;
44667             }
44668             tb.addField( new Roo.form.TextField({
44669                 name: '-roo-edit-' + i,
44670                 attrname : i,
44671                 
44672                 width: item.width,
44673                 //allowBlank:true,
44674                 value: '',
44675                 listeners: {
44676                     'change' : function(f, nv, ov) {
44677                         tb.selectedNode.setAttribute(f.attrname, nv);
44678                     }
44679                 }
44680             }));
44681              
44682         }
44683         
44684         var _this = this;
44685         
44686         if(nm == 'BODY'){
44687             tb.addSeparator();
44688         
44689             tb.addButton( {
44690                 text: 'Stylesheets',
44691
44692                 listeners : {
44693                     click : function ()
44694                     {
44695                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44696                     }
44697                 }
44698             });
44699         }
44700         
44701         tb.addFill();
44702         tb.addButton( {
44703             text: 'Remove Tag',
44704     
44705             listeners : {
44706                 click : function ()
44707                 {
44708                     // remove
44709                     // undo does not work.
44710                      
44711                     var sn = tb.selectedNode;
44712                     
44713                     var pn = sn.parentNode;
44714                     
44715                     var stn =  sn.childNodes[0];
44716                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44717                     while (sn.childNodes.length) {
44718                         var node = sn.childNodes[0];
44719                         sn.removeChild(node);
44720                         //Roo.log(node);
44721                         pn.insertBefore(node, sn);
44722                         
44723                     }
44724                     pn.removeChild(sn);
44725                     var range = editorcore.createRange();
44726         
44727                     range.setStart(stn,0);
44728                     range.setEnd(en,0); //????
44729                     //range.selectNode(sel);
44730                     
44731                     
44732                     var selection = editorcore.getSelection();
44733                     selection.removeAllRanges();
44734                     selection.addRange(range);
44735                     
44736                     
44737                     
44738                     //_this.updateToolbar(null, null, pn);
44739                     _this.updateToolbar(null, null, null);
44740                     _this.footDisp.dom.innerHTML = ''; 
44741                 }
44742             }
44743             
44744                     
44745                 
44746             
44747         });
44748         
44749         
44750         tb.el.on('click', function(e){
44751             e.preventDefault(); // what does this do?
44752         });
44753         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44754         tb.el.hide();
44755         tb.name = nm;
44756         // dont need to disable them... as they will get hidden
44757         return tb;
44758          
44759         
44760     },
44761     buildFooter : function()
44762     {
44763         
44764         var fel = this.editor.wrap.createChild();
44765         this.footer = new Roo.Toolbar(fel);
44766         // toolbar has scrolly on left / right?
44767         var footDisp= new Roo.Toolbar.Fill();
44768         var _t = this;
44769         this.footer.add(
44770             {
44771                 text : '&lt;',
44772                 xtype: 'Button',
44773                 handler : function() {
44774                     _t.footDisp.scrollTo('left',0,true)
44775                 }
44776             }
44777         );
44778         this.footer.add( footDisp );
44779         this.footer.add( 
44780             {
44781                 text : '&gt;',
44782                 xtype: 'Button',
44783                 handler : function() {
44784                     // no animation..
44785                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44786                 }
44787             }
44788         );
44789         var fel = Roo.get(footDisp.el);
44790         fel.addClass('x-editor-context');
44791         this.footDispWrap = fel; 
44792         this.footDispWrap.overflow  = 'hidden';
44793         
44794         this.footDisp = fel.createChild();
44795         this.footDispWrap.on('click', this.onContextClick, this)
44796         
44797         
44798     },
44799     onContextClick : function (ev,dom)
44800     {
44801         ev.preventDefault();
44802         var  cn = dom.className;
44803         //Roo.log(cn);
44804         if (!cn.match(/x-ed-loc-/)) {
44805             return;
44806         }
44807         var n = cn.split('-').pop();
44808         var ans = this.footerEls;
44809         var sel = ans[n];
44810         
44811          // pick
44812         var range = this.editorcore.createRange();
44813         
44814         range.selectNodeContents(sel);
44815         //range.selectNode(sel);
44816         
44817         
44818         var selection = this.editorcore.getSelection();
44819         selection.removeAllRanges();
44820         selection.addRange(range);
44821         
44822         
44823         
44824         this.updateToolbar(null, null, sel);
44825         
44826         
44827     }
44828     
44829     
44830     
44831     
44832     
44833 });
44834
44835
44836
44837
44838
44839 /*
44840  * Based on:
44841  * Ext JS Library 1.1.1
44842  * Copyright(c) 2006-2007, Ext JS, LLC.
44843  *
44844  * Originally Released Under LGPL - original licence link has changed is not relivant.
44845  *
44846  * Fork - LGPL
44847  * <script type="text/javascript">
44848  */
44849  
44850 /**
44851  * @class Roo.form.BasicForm
44852  * @extends Roo.util.Observable
44853  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44854  * @constructor
44855  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44856  * @param {Object} config Configuration options
44857  */
44858 Roo.form.BasicForm = function(el, config){
44859     this.allItems = [];
44860     this.childForms = [];
44861     Roo.apply(this, config);
44862     /*
44863      * The Roo.form.Field items in this form.
44864      * @type MixedCollection
44865      */
44866      
44867      
44868     this.items = new Roo.util.MixedCollection(false, function(o){
44869         return o.id || (o.id = Roo.id());
44870     });
44871     this.addEvents({
44872         /**
44873          * @event beforeaction
44874          * Fires before any action is performed. Return false to cancel the action.
44875          * @param {Form} this
44876          * @param {Action} action The action to be performed
44877          */
44878         beforeaction: true,
44879         /**
44880          * @event actionfailed
44881          * Fires when an action fails.
44882          * @param {Form} this
44883          * @param {Action} action The action that failed
44884          */
44885         actionfailed : true,
44886         /**
44887          * @event actioncomplete
44888          * Fires when an action is completed.
44889          * @param {Form} this
44890          * @param {Action} action The action that completed
44891          */
44892         actioncomplete : true
44893     });
44894     if(el){
44895         this.initEl(el);
44896     }
44897     Roo.form.BasicForm.superclass.constructor.call(this);
44898 };
44899
44900 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44901     /**
44902      * @cfg {String} method
44903      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44904      */
44905     /**
44906      * @cfg {DataReader} reader
44907      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44908      * This is optional as there is built-in support for processing JSON.
44909      */
44910     /**
44911      * @cfg {DataReader} errorReader
44912      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44913      * This is completely optional as there is built-in support for processing JSON.
44914      */
44915     /**
44916      * @cfg {String} url
44917      * The URL to use for form actions if one isn't supplied in the action options.
44918      */
44919     /**
44920      * @cfg {Boolean} fileUpload
44921      * Set to true if this form is a file upload.
44922      */
44923      
44924     /**
44925      * @cfg {Object} baseParams
44926      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44927      */
44928      /**
44929      
44930     /**
44931      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44932      */
44933     timeout: 30,
44934
44935     // private
44936     activeAction : null,
44937
44938     /**
44939      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44940      * or setValues() data instead of when the form was first created.
44941      */
44942     trackResetOnLoad : false,
44943     
44944     
44945     /**
44946      * childForms - used for multi-tab forms
44947      * @type {Array}
44948      */
44949     childForms : false,
44950     
44951     /**
44952      * allItems - full list of fields.
44953      * @type {Array}
44954      */
44955     allItems : false,
44956     
44957     /**
44958      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44959      * element by passing it or its id or mask the form itself by passing in true.
44960      * @type Mixed
44961      */
44962     waitMsgTarget : false,
44963
44964     // private
44965     initEl : function(el){
44966         this.el = Roo.get(el);
44967         this.id = this.el.id || Roo.id();
44968         this.el.on('submit', this.onSubmit, this);
44969         this.el.addClass('x-form');
44970     },
44971
44972     // private
44973     onSubmit : function(e){
44974         e.stopEvent();
44975     },
44976
44977     /**
44978      * Returns true if client-side validation on the form is successful.
44979      * @return Boolean
44980      */
44981     isValid : function(){
44982         var valid = true;
44983         this.items.each(function(f){
44984            if(!f.validate()){
44985                valid = false;
44986            }
44987         });
44988         return valid;
44989     },
44990
44991     /**
44992      * Returns true if any fields in this form have changed since their original load.
44993      * @return Boolean
44994      */
44995     isDirty : function(){
44996         var dirty = false;
44997         this.items.each(function(f){
44998            if(f.isDirty()){
44999                dirty = true;
45000                return false;
45001            }
45002         });
45003         return dirty;
45004     },
45005
45006     /**
45007      * Performs a predefined action (submit or load) or custom actions you define on this form.
45008      * @param {String} actionName The name of the action type
45009      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45010      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45011      * accept other config options):
45012      * <pre>
45013 Property          Type             Description
45014 ----------------  ---------------  ----------------------------------------------------------------------------------
45015 url               String           The url for the action (defaults to the form's url)
45016 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45017 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45018 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45019                                    validate the form on the client (defaults to false)
45020      * </pre>
45021      * @return {BasicForm} this
45022      */
45023     doAction : function(action, options){
45024         if(typeof action == 'string'){
45025             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45026         }
45027         if(this.fireEvent('beforeaction', this, action) !== false){
45028             this.beforeAction(action);
45029             action.run.defer(100, action);
45030         }
45031         return this;
45032     },
45033
45034     /**
45035      * Shortcut to do a submit action.
45036      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45037      * @return {BasicForm} this
45038      */
45039     submit : function(options){
45040         this.doAction('submit', options);
45041         return this;
45042     },
45043
45044     /**
45045      * Shortcut to do a load action.
45046      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45047      * @return {BasicForm} this
45048      */
45049     load : function(options){
45050         this.doAction('load', options);
45051         return this;
45052     },
45053
45054     /**
45055      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45056      * @param {Record} record The record to edit
45057      * @return {BasicForm} this
45058      */
45059     updateRecord : function(record){
45060         record.beginEdit();
45061         var fs = record.fields;
45062         fs.each(function(f){
45063             var field = this.findField(f.name);
45064             if(field){
45065                 record.set(f.name, field.getValue());
45066             }
45067         }, this);
45068         record.endEdit();
45069         return this;
45070     },
45071
45072     /**
45073      * Loads an Roo.data.Record into this form.
45074      * @param {Record} record The record to load
45075      * @return {BasicForm} this
45076      */
45077     loadRecord : function(record){
45078         this.setValues(record.data);
45079         return this;
45080     },
45081
45082     // private
45083     beforeAction : function(action){
45084         var o = action.options;
45085         
45086        
45087         if(this.waitMsgTarget === true){
45088             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45089         }else if(this.waitMsgTarget){
45090             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45091             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45092         }else {
45093             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45094         }
45095          
45096     },
45097
45098     // private
45099     afterAction : function(action, success){
45100         this.activeAction = null;
45101         var o = action.options;
45102         
45103         if(this.waitMsgTarget === true){
45104             this.el.unmask();
45105         }else if(this.waitMsgTarget){
45106             this.waitMsgTarget.unmask();
45107         }else{
45108             Roo.MessageBox.updateProgress(1);
45109             Roo.MessageBox.hide();
45110         }
45111          
45112         if(success){
45113             if(o.reset){
45114                 this.reset();
45115             }
45116             Roo.callback(o.success, o.scope, [this, action]);
45117             this.fireEvent('actioncomplete', this, action);
45118             
45119         }else{
45120             
45121             // failure condition..
45122             // we have a scenario where updates need confirming.
45123             // eg. if a locking scenario exists..
45124             // we look for { errors : { needs_confirm : true }} in the response.
45125             if (
45126                 (typeof(action.result) != 'undefined')  &&
45127                 (typeof(action.result.errors) != 'undefined')  &&
45128                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45129            ){
45130                 var _t = this;
45131                 Roo.MessageBox.confirm(
45132                     "Change requires confirmation",
45133                     action.result.errorMsg,
45134                     function(r) {
45135                         if (r != 'yes') {
45136                             return;
45137                         }
45138                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45139                     }
45140                     
45141                 );
45142                 
45143                 
45144                 
45145                 return;
45146             }
45147             
45148             Roo.callback(o.failure, o.scope, [this, action]);
45149             // show an error message if no failed handler is set..
45150             if (!this.hasListener('actionfailed')) {
45151                 Roo.MessageBox.alert("Error",
45152                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45153                         action.result.errorMsg :
45154                         "Saving Failed, please check your entries or try again"
45155                 );
45156             }
45157             
45158             this.fireEvent('actionfailed', this, action);
45159         }
45160         
45161     },
45162
45163     /**
45164      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45165      * @param {String} id The value to search for
45166      * @return Field
45167      */
45168     findField : function(id){
45169         var field = this.items.get(id);
45170         if(!field){
45171             this.items.each(function(f){
45172                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45173                     field = f;
45174                     return false;
45175                 }
45176             });
45177         }
45178         return field || null;
45179     },
45180
45181     /**
45182      * Add a secondary form to this one, 
45183      * Used to provide tabbed forms. One form is primary, with hidden values 
45184      * which mirror the elements from the other forms.
45185      * 
45186      * @param {Roo.form.Form} form to add.
45187      * 
45188      */
45189     addForm : function(form)
45190     {
45191        
45192         if (this.childForms.indexOf(form) > -1) {
45193             // already added..
45194             return;
45195         }
45196         this.childForms.push(form);
45197         var n = '';
45198         Roo.each(form.allItems, function (fe) {
45199             
45200             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45201             if (this.findField(n)) { // already added..
45202                 return;
45203             }
45204             var add = new Roo.form.Hidden({
45205                 name : n
45206             });
45207             add.render(this.el);
45208             
45209             this.add( add );
45210         }, this);
45211         
45212     },
45213     /**
45214      * Mark fields in this form invalid in bulk.
45215      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45216      * @return {BasicForm} this
45217      */
45218     markInvalid : function(errors){
45219         if(errors instanceof Array){
45220             for(var i = 0, len = errors.length; i < len; i++){
45221                 var fieldError = errors[i];
45222                 var f = this.findField(fieldError.id);
45223                 if(f){
45224                     f.markInvalid(fieldError.msg);
45225                 }
45226             }
45227         }else{
45228             var field, id;
45229             for(id in errors){
45230                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45231                     field.markInvalid(errors[id]);
45232                 }
45233             }
45234         }
45235         Roo.each(this.childForms || [], function (f) {
45236             f.markInvalid(errors);
45237         });
45238         
45239         return this;
45240     },
45241
45242     /**
45243      * Set values for fields in this form in bulk.
45244      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45245      * @return {BasicForm} this
45246      */
45247     setValues : function(values){
45248         if(values instanceof Array){ // array of objects
45249             for(var i = 0, len = values.length; i < len; i++){
45250                 var v = values[i];
45251                 var f = this.findField(v.id);
45252                 if(f){
45253                     f.setValue(v.value);
45254                     if(this.trackResetOnLoad){
45255                         f.originalValue = f.getValue();
45256                     }
45257                 }
45258             }
45259         }else{ // object hash
45260             var field, id;
45261             for(id in values){
45262                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45263                     
45264                     if (field.setFromData && 
45265                         field.valueField && 
45266                         field.displayField &&
45267                         // combos' with local stores can 
45268                         // be queried via setValue()
45269                         // to set their value..
45270                         (field.store && !field.store.isLocal)
45271                         ) {
45272                         // it's a combo
45273                         var sd = { };
45274                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45275                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45276                         field.setFromData(sd);
45277                         
45278                     } else {
45279                         field.setValue(values[id]);
45280                     }
45281                     
45282                     
45283                     if(this.trackResetOnLoad){
45284                         field.originalValue = field.getValue();
45285                     }
45286                 }
45287             }
45288         }
45289          
45290         Roo.each(this.childForms || [], function (f) {
45291             f.setValues(values);
45292         });
45293                 
45294         return this;
45295     },
45296
45297     /**
45298      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45299      * they are returned as an array.
45300      * @param {Boolean} asString
45301      * @return {Object}
45302      */
45303     getValues : function(asString){
45304         if (this.childForms) {
45305             // copy values from the child forms
45306             Roo.each(this.childForms, function (f) {
45307                 this.setValues(f.getValues());
45308             }, this);
45309         }
45310         
45311         
45312         
45313         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45314         if(asString === true){
45315             return fs;
45316         }
45317         return Roo.urlDecode(fs);
45318     },
45319     
45320     /**
45321      * Returns the fields in this form as an object with key/value pairs. 
45322      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45323      * @return {Object}
45324      */
45325     getFieldValues : function(with_hidden)
45326     {
45327         if (this.childForms) {
45328             // copy values from the child forms
45329             // should this call getFieldValues - probably not as we do not currently copy
45330             // hidden fields when we generate..
45331             Roo.each(this.childForms, function (f) {
45332                 this.setValues(f.getValues());
45333             }, this);
45334         }
45335         
45336         var ret = {};
45337         this.items.each(function(f){
45338             if (!f.getName()) {
45339                 return;
45340             }
45341             var v = f.getValue();
45342             if (f.inputType =='radio') {
45343                 if (typeof(ret[f.getName()]) == 'undefined') {
45344                     ret[f.getName()] = ''; // empty..
45345                 }
45346                 
45347                 if (!f.el.dom.checked) {
45348                     return;
45349                     
45350                 }
45351                 v = f.el.dom.value;
45352                 
45353             }
45354             
45355             // not sure if this supported any more..
45356             if ((typeof(v) == 'object') && f.getRawValue) {
45357                 v = f.getRawValue() ; // dates..
45358             }
45359             // combo boxes where name != hiddenName...
45360             if (f.name != f.getName()) {
45361                 ret[f.name] = f.getRawValue();
45362             }
45363             ret[f.getName()] = v;
45364         });
45365         
45366         return ret;
45367     },
45368
45369     /**
45370      * Clears all invalid messages in this form.
45371      * @return {BasicForm} this
45372      */
45373     clearInvalid : function(){
45374         this.items.each(function(f){
45375            f.clearInvalid();
45376         });
45377         
45378         Roo.each(this.childForms || [], function (f) {
45379             f.clearInvalid();
45380         });
45381         
45382         
45383         return this;
45384     },
45385
45386     /**
45387      * Resets this form.
45388      * @return {BasicForm} this
45389      */
45390     reset : function(){
45391         this.items.each(function(f){
45392             f.reset();
45393         });
45394         
45395         Roo.each(this.childForms || [], function (f) {
45396             f.reset();
45397         });
45398        
45399         
45400         return this;
45401     },
45402
45403     /**
45404      * Add Roo.form components to this form.
45405      * @param {Field} field1
45406      * @param {Field} field2 (optional)
45407      * @param {Field} etc (optional)
45408      * @return {BasicForm} this
45409      */
45410     add : function(){
45411         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45412         return this;
45413     },
45414
45415
45416     /**
45417      * Removes a field from the items collection (does NOT remove its markup).
45418      * @param {Field} field
45419      * @return {BasicForm} this
45420      */
45421     remove : function(field){
45422         this.items.remove(field);
45423         return this;
45424     },
45425
45426     /**
45427      * Looks at the fields in this form, checks them for an id attribute,
45428      * and calls applyTo on the existing dom element with that id.
45429      * @return {BasicForm} this
45430      */
45431     render : function(){
45432         this.items.each(function(f){
45433             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45434                 f.applyTo(f.id);
45435             }
45436         });
45437         return this;
45438     },
45439
45440     /**
45441      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45442      * @param {Object} values
45443      * @return {BasicForm} this
45444      */
45445     applyToFields : function(o){
45446         this.items.each(function(f){
45447            Roo.apply(f, o);
45448         });
45449         return this;
45450     },
45451
45452     /**
45453      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45454      * @param {Object} values
45455      * @return {BasicForm} this
45456      */
45457     applyIfToFields : function(o){
45458         this.items.each(function(f){
45459            Roo.applyIf(f, o);
45460         });
45461         return this;
45462     }
45463 });
45464
45465 // back compat
45466 Roo.BasicForm = Roo.form.BasicForm;/*
45467  * Based on:
45468  * Ext JS Library 1.1.1
45469  * Copyright(c) 2006-2007, Ext JS, LLC.
45470  *
45471  * Originally Released Under LGPL - original licence link has changed is not relivant.
45472  *
45473  * Fork - LGPL
45474  * <script type="text/javascript">
45475  */
45476
45477 /**
45478  * @class Roo.form.Form
45479  * @extends Roo.form.BasicForm
45480  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45481  * @constructor
45482  * @param {Object} config Configuration options
45483  */
45484 Roo.form.Form = function(config){
45485     var xitems =  [];
45486     if (config.items) {
45487         xitems = config.items;
45488         delete config.items;
45489     }
45490    
45491     
45492     Roo.form.Form.superclass.constructor.call(this, null, config);
45493     this.url = this.url || this.action;
45494     if(!this.root){
45495         this.root = new Roo.form.Layout(Roo.applyIf({
45496             id: Roo.id()
45497         }, config));
45498     }
45499     this.active = this.root;
45500     /**
45501      * Array of all the buttons that have been added to this form via {@link addButton}
45502      * @type Array
45503      */
45504     this.buttons = [];
45505     this.allItems = [];
45506     this.addEvents({
45507         /**
45508          * @event clientvalidation
45509          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45510          * @param {Form} this
45511          * @param {Boolean} valid true if the form has passed client-side validation
45512          */
45513         clientvalidation: true,
45514         /**
45515          * @event rendered
45516          * Fires when the form is rendered
45517          * @param {Roo.form.Form} form
45518          */
45519         rendered : true
45520     });
45521     
45522     if (this.progressUrl) {
45523             // push a hidden field onto the list of fields..
45524             this.addxtype( {
45525                     xns: Roo.form, 
45526                     xtype : 'Hidden', 
45527                     name : 'UPLOAD_IDENTIFIER' 
45528             });
45529         }
45530         
45531     
45532     Roo.each(xitems, this.addxtype, this);
45533     
45534     
45535     
45536 };
45537
45538 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45539     /**
45540      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45541      */
45542     /**
45543      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45544      */
45545     /**
45546      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45547      */
45548     buttonAlign:'center',
45549
45550     /**
45551      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45552      */
45553     minButtonWidth:75,
45554
45555     /**
45556      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45557      * This property cascades to child containers if not set.
45558      */
45559     labelAlign:'left',
45560
45561     /**
45562      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45563      * fires a looping event with that state. This is required to bind buttons to the valid
45564      * state using the config value formBind:true on the button.
45565      */
45566     monitorValid : false,
45567
45568     /**
45569      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45570      */
45571     monitorPoll : 200,
45572     
45573     /**
45574      * @cfg {String} progressUrl - Url to return progress data 
45575      */
45576     
45577     progressUrl : false,
45578   
45579     /**
45580      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45581      * fields are added and the column is closed. If no fields are passed the column remains open
45582      * until end() is called.
45583      * @param {Object} config The config to pass to the column
45584      * @param {Field} field1 (optional)
45585      * @param {Field} field2 (optional)
45586      * @param {Field} etc (optional)
45587      * @return Column The column container object
45588      */
45589     column : function(c){
45590         var col = new Roo.form.Column(c);
45591         this.start(col);
45592         if(arguments.length > 1){ // duplicate code required because of Opera
45593             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45594             this.end();
45595         }
45596         return col;
45597     },
45598
45599     /**
45600      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45601      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45602      * until end() is called.
45603      * @param {Object} config The config to pass to the fieldset
45604      * @param {Field} field1 (optional)
45605      * @param {Field} field2 (optional)
45606      * @param {Field} etc (optional)
45607      * @return FieldSet The fieldset container object
45608      */
45609     fieldset : function(c){
45610         var fs = new Roo.form.FieldSet(c);
45611         this.start(fs);
45612         if(arguments.length > 1){ // duplicate code required because of Opera
45613             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45614             this.end();
45615         }
45616         return fs;
45617     },
45618
45619     /**
45620      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45621      * fields are added and the container is closed. If no fields are passed the container remains open
45622      * until end() is called.
45623      * @param {Object} config The config to pass to the Layout
45624      * @param {Field} field1 (optional)
45625      * @param {Field} field2 (optional)
45626      * @param {Field} etc (optional)
45627      * @return Layout The container object
45628      */
45629     container : function(c){
45630         var l = new Roo.form.Layout(c);
45631         this.start(l);
45632         if(arguments.length > 1){ // duplicate code required because of Opera
45633             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45634             this.end();
45635         }
45636         return l;
45637     },
45638
45639     /**
45640      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45641      * @param {Object} container A Roo.form.Layout or subclass of Layout
45642      * @return {Form} this
45643      */
45644     start : function(c){
45645         // cascade label info
45646         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45647         this.active.stack.push(c);
45648         c.ownerCt = this.active;
45649         this.active = c;
45650         return this;
45651     },
45652
45653     /**
45654      * Closes the current open container
45655      * @return {Form} this
45656      */
45657     end : function(){
45658         if(this.active == this.root){
45659             return this;
45660         }
45661         this.active = this.active.ownerCt;
45662         return this;
45663     },
45664
45665     /**
45666      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45667      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45668      * as the label of the field.
45669      * @param {Field} field1
45670      * @param {Field} field2 (optional)
45671      * @param {Field} etc. (optional)
45672      * @return {Form} this
45673      */
45674     add : function(){
45675         this.active.stack.push.apply(this.active.stack, arguments);
45676         this.allItems.push.apply(this.allItems,arguments);
45677         var r = [];
45678         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45679             if(a[i].isFormField){
45680                 r.push(a[i]);
45681             }
45682         }
45683         if(r.length > 0){
45684             Roo.form.Form.superclass.add.apply(this, r);
45685         }
45686         return this;
45687     },
45688     
45689
45690     
45691     
45692     
45693      /**
45694      * Find any element that has been added to a form, using it's ID or name
45695      * This can include framesets, columns etc. along with regular fields..
45696      * @param {String} id - id or name to find.
45697      
45698      * @return {Element} e - or false if nothing found.
45699      */
45700     findbyId : function(id)
45701     {
45702         var ret = false;
45703         if (!id) {
45704             return ret;
45705         }
45706         Roo.each(this.allItems, function(f){
45707             if (f.id == id || f.name == id ){
45708                 ret = f;
45709                 return false;
45710             }
45711         });
45712         return ret;
45713     },
45714
45715     
45716     
45717     /**
45718      * Render this form into the passed container. This should only be called once!
45719      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45720      * @return {Form} this
45721      */
45722     render : function(ct)
45723     {
45724         
45725         
45726         
45727         ct = Roo.get(ct);
45728         var o = this.autoCreate || {
45729             tag: 'form',
45730             method : this.method || 'POST',
45731             id : this.id || Roo.id()
45732         };
45733         this.initEl(ct.createChild(o));
45734
45735         this.root.render(this.el);
45736         
45737        
45738              
45739         this.items.each(function(f){
45740             f.render('x-form-el-'+f.id);
45741         });
45742
45743         if(this.buttons.length > 0){
45744             // tables are required to maintain order and for correct IE layout
45745             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45746                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45747                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45748             }}, null, true);
45749             var tr = tb.getElementsByTagName('tr')[0];
45750             for(var i = 0, len = this.buttons.length; i < len; i++) {
45751                 var b = this.buttons[i];
45752                 var td = document.createElement('td');
45753                 td.className = 'x-form-btn-td';
45754                 b.render(tr.appendChild(td));
45755             }
45756         }
45757         if(this.monitorValid){ // initialize after render
45758             this.startMonitoring();
45759         }
45760         this.fireEvent('rendered', this);
45761         return this;
45762     },
45763
45764     /**
45765      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45766      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45767      * object or a valid Roo.DomHelper element config
45768      * @param {Function} handler The function called when the button is clicked
45769      * @param {Object} scope (optional) The scope of the handler function
45770      * @return {Roo.Button}
45771      */
45772     addButton : function(config, handler, scope){
45773         var bc = {
45774             handler: handler,
45775             scope: scope,
45776             minWidth: this.minButtonWidth,
45777             hideParent:true
45778         };
45779         if(typeof config == "string"){
45780             bc.text = config;
45781         }else{
45782             Roo.apply(bc, config);
45783         }
45784         var btn = new Roo.Button(null, bc);
45785         this.buttons.push(btn);
45786         return btn;
45787     },
45788
45789      /**
45790      * Adds a series of form elements (using the xtype property as the factory method.
45791      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45792      * @param {Object} config 
45793      */
45794     
45795     addxtype : function()
45796     {
45797         var ar = Array.prototype.slice.call(arguments, 0);
45798         var ret = false;
45799         for(var i = 0; i < ar.length; i++) {
45800             if (!ar[i]) {
45801                 continue; // skip -- if this happends something invalid got sent, we 
45802                 // should ignore it, as basically that interface element will not show up
45803                 // and that should be pretty obvious!!
45804             }
45805             
45806             if (Roo.form[ar[i].xtype]) {
45807                 ar[i].form = this;
45808                 var fe = Roo.factory(ar[i], Roo.form);
45809                 if (!ret) {
45810                     ret = fe;
45811                 }
45812                 fe.form = this;
45813                 if (fe.store) {
45814                     fe.store.form = this;
45815                 }
45816                 if (fe.isLayout) {  
45817                          
45818                     this.start(fe);
45819                     this.allItems.push(fe);
45820                     if (fe.items && fe.addxtype) {
45821                         fe.addxtype.apply(fe, fe.items);
45822                         delete fe.items;
45823                     }
45824                      this.end();
45825                     continue;
45826                 }
45827                 
45828                 
45829                  
45830                 this.add(fe);
45831               //  console.log('adding ' + ar[i].xtype);
45832             }
45833             if (ar[i].xtype == 'Button') {  
45834                 //console.log('adding button');
45835                 //console.log(ar[i]);
45836                 this.addButton(ar[i]);
45837                 this.allItems.push(fe);
45838                 continue;
45839             }
45840             
45841             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45842                 alert('end is not supported on xtype any more, use items');
45843             //    this.end();
45844             //    //console.log('adding end');
45845             }
45846             
45847         }
45848         return ret;
45849     },
45850     
45851     /**
45852      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45853      * option "monitorValid"
45854      */
45855     startMonitoring : function(){
45856         if(!this.bound){
45857             this.bound = true;
45858             Roo.TaskMgr.start({
45859                 run : this.bindHandler,
45860                 interval : this.monitorPoll || 200,
45861                 scope: this
45862             });
45863         }
45864     },
45865
45866     /**
45867      * Stops monitoring of the valid state of this form
45868      */
45869     stopMonitoring : function(){
45870         this.bound = false;
45871     },
45872
45873     // private
45874     bindHandler : function(){
45875         if(!this.bound){
45876             return false; // stops binding
45877         }
45878         var valid = true;
45879         this.items.each(function(f){
45880             if(!f.isValid(true)){
45881                 valid = false;
45882                 return false;
45883             }
45884         });
45885         for(var i = 0, len = this.buttons.length; i < len; i++){
45886             var btn = this.buttons[i];
45887             if(btn.formBind === true && btn.disabled === valid){
45888                 btn.setDisabled(!valid);
45889             }
45890         }
45891         this.fireEvent('clientvalidation', this, valid);
45892     }
45893     
45894     
45895     
45896     
45897     
45898     
45899     
45900     
45901 });
45902
45903
45904 // back compat
45905 Roo.Form = Roo.form.Form;
45906 /*
45907  * Based on:
45908  * Ext JS Library 1.1.1
45909  * Copyright(c) 2006-2007, Ext JS, LLC.
45910  *
45911  * Originally Released Under LGPL - original licence link has changed is not relivant.
45912  *
45913  * Fork - LGPL
45914  * <script type="text/javascript">
45915  */
45916
45917 // as we use this in bootstrap.
45918 Roo.namespace('Roo.form');
45919  /**
45920  * @class Roo.form.Action
45921  * Internal Class used to handle form actions
45922  * @constructor
45923  * @param {Roo.form.BasicForm} el The form element or its id
45924  * @param {Object} config Configuration options
45925  */
45926
45927  
45928  
45929 // define the action interface
45930 Roo.form.Action = function(form, options){
45931     this.form = form;
45932     this.options = options || {};
45933 };
45934 /**
45935  * Client Validation Failed
45936  * @const 
45937  */
45938 Roo.form.Action.CLIENT_INVALID = 'client';
45939 /**
45940  * Server Validation Failed
45941  * @const 
45942  */
45943 Roo.form.Action.SERVER_INVALID = 'server';
45944  /**
45945  * Connect to Server Failed
45946  * @const 
45947  */
45948 Roo.form.Action.CONNECT_FAILURE = 'connect';
45949 /**
45950  * Reading Data from Server Failed
45951  * @const 
45952  */
45953 Roo.form.Action.LOAD_FAILURE = 'load';
45954
45955 Roo.form.Action.prototype = {
45956     type : 'default',
45957     failureType : undefined,
45958     response : undefined,
45959     result : undefined,
45960
45961     // interface method
45962     run : function(options){
45963
45964     },
45965
45966     // interface method
45967     success : function(response){
45968
45969     },
45970
45971     // interface method
45972     handleResponse : function(response){
45973
45974     },
45975
45976     // default connection failure
45977     failure : function(response){
45978         
45979         this.response = response;
45980         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45981         this.form.afterAction(this, false);
45982     },
45983
45984     processResponse : function(response){
45985         this.response = response;
45986         if(!response.responseText){
45987             return true;
45988         }
45989         this.result = this.handleResponse(response);
45990         return this.result;
45991     },
45992
45993     // utility functions used internally
45994     getUrl : function(appendParams){
45995         var url = this.options.url || this.form.url || this.form.el.dom.action;
45996         if(appendParams){
45997             var p = this.getParams();
45998             if(p){
45999                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46000             }
46001         }
46002         return url;
46003     },
46004
46005     getMethod : function(){
46006         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46007     },
46008
46009     getParams : function(){
46010         var bp = this.form.baseParams;
46011         var p = this.options.params;
46012         if(p){
46013             if(typeof p == "object"){
46014                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46015             }else if(typeof p == 'string' && bp){
46016                 p += '&' + Roo.urlEncode(bp);
46017             }
46018         }else if(bp){
46019             p = Roo.urlEncode(bp);
46020         }
46021         return p;
46022     },
46023
46024     createCallback : function(){
46025         return {
46026             success: this.success,
46027             failure: this.failure,
46028             scope: this,
46029             timeout: (this.form.timeout*1000),
46030             upload: this.form.fileUpload ? this.success : undefined
46031         };
46032     }
46033 };
46034
46035 Roo.form.Action.Submit = function(form, options){
46036     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46037 };
46038
46039 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46040     type : 'submit',
46041
46042     haveProgress : false,
46043     uploadComplete : false,
46044     
46045     // uploadProgress indicator.
46046     uploadProgress : function()
46047     {
46048         if (!this.form.progressUrl) {
46049             return;
46050         }
46051         
46052         if (!this.haveProgress) {
46053             Roo.MessageBox.progress("Uploading", "Uploading");
46054         }
46055         if (this.uploadComplete) {
46056            Roo.MessageBox.hide();
46057            return;
46058         }
46059         
46060         this.haveProgress = true;
46061    
46062         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46063         
46064         var c = new Roo.data.Connection();
46065         c.request({
46066             url : this.form.progressUrl,
46067             params: {
46068                 id : uid
46069             },
46070             method: 'GET',
46071             success : function(req){
46072                //console.log(data);
46073                 var rdata = false;
46074                 var edata;
46075                 try  {
46076                    rdata = Roo.decode(req.responseText)
46077                 } catch (e) {
46078                     Roo.log("Invalid data from server..");
46079                     Roo.log(edata);
46080                     return;
46081                 }
46082                 if (!rdata || !rdata.success) {
46083                     Roo.log(rdata);
46084                     Roo.MessageBox.alert(Roo.encode(rdata));
46085                     return;
46086                 }
46087                 var data = rdata.data;
46088                 
46089                 if (this.uploadComplete) {
46090                    Roo.MessageBox.hide();
46091                    return;
46092                 }
46093                    
46094                 if (data){
46095                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46096                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46097                     );
46098                 }
46099                 this.uploadProgress.defer(2000,this);
46100             },
46101        
46102             failure: function(data) {
46103                 Roo.log('progress url failed ');
46104                 Roo.log(data);
46105             },
46106             scope : this
46107         });
46108            
46109     },
46110     
46111     
46112     run : function()
46113     {
46114         // run get Values on the form, so it syncs any secondary forms.
46115         this.form.getValues();
46116         
46117         var o = this.options;
46118         var method = this.getMethod();
46119         var isPost = method == 'POST';
46120         if(o.clientValidation === false || this.form.isValid()){
46121             
46122             if (this.form.progressUrl) {
46123                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46124                     (new Date() * 1) + '' + Math.random());
46125                     
46126             } 
46127             
46128             
46129             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46130                 form:this.form.el.dom,
46131                 url:this.getUrl(!isPost),
46132                 method: method,
46133                 params:isPost ? this.getParams() : null,
46134                 isUpload: this.form.fileUpload
46135             }));
46136             
46137             this.uploadProgress();
46138
46139         }else if (o.clientValidation !== false){ // client validation failed
46140             this.failureType = Roo.form.Action.CLIENT_INVALID;
46141             this.form.afterAction(this, false);
46142         }
46143     },
46144
46145     success : function(response)
46146     {
46147         this.uploadComplete= true;
46148         if (this.haveProgress) {
46149             Roo.MessageBox.hide();
46150         }
46151         
46152         
46153         var result = this.processResponse(response);
46154         if(result === true || result.success){
46155             this.form.afterAction(this, true);
46156             return;
46157         }
46158         if(result.errors){
46159             this.form.markInvalid(result.errors);
46160             this.failureType = Roo.form.Action.SERVER_INVALID;
46161         }
46162         this.form.afterAction(this, false);
46163     },
46164     failure : function(response)
46165     {
46166         this.uploadComplete= true;
46167         if (this.haveProgress) {
46168             Roo.MessageBox.hide();
46169         }
46170         
46171         this.response = response;
46172         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46173         this.form.afterAction(this, false);
46174     },
46175     
46176     handleResponse : function(response){
46177         if(this.form.errorReader){
46178             var rs = this.form.errorReader.read(response);
46179             var errors = [];
46180             if(rs.records){
46181                 for(var i = 0, len = rs.records.length; i < len; i++) {
46182                     var r = rs.records[i];
46183                     errors[i] = r.data;
46184                 }
46185             }
46186             if(errors.length < 1){
46187                 errors = null;
46188             }
46189             return {
46190                 success : rs.success,
46191                 errors : errors
46192             };
46193         }
46194         var ret = false;
46195         try {
46196             ret = Roo.decode(response.responseText);
46197         } catch (e) {
46198             ret = {
46199                 success: false,
46200                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46201                 errors : []
46202             };
46203         }
46204         return ret;
46205         
46206     }
46207 });
46208
46209
46210 Roo.form.Action.Load = function(form, options){
46211     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46212     this.reader = this.form.reader;
46213 };
46214
46215 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46216     type : 'load',
46217
46218     run : function(){
46219         
46220         Roo.Ajax.request(Roo.apply(
46221                 this.createCallback(), {
46222                     method:this.getMethod(),
46223                     url:this.getUrl(false),
46224                     params:this.getParams()
46225         }));
46226     },
46227
46228     success : function(response){
46229         
46230         var result = this.processResponse(response);
46231         if(result === true || !result.success || !result.data){
46232             this.failureType = Roo.form.Action.LOAD_FAILURE;
46233             this.form.afterAction(this, false);
46234             return;
46235         }
46236         this.form.clearInvalid();
46237         this.form.setValues(result.data);
46238         this.form.afterAction(this, true);
46239     },
46240
46241     handleResponse : function(response){
46242         if(this.form.reader){
46243             var rs = this.form.reader.read(response);
46244             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46245             return {
46246                 success : rs.success,
46247                 data : data
46248             };
46249         }
46250         return Roo.decode(response.responseText);
46251     }
46252 });
46253
46254 Roo.form.Action.ACTION_TYPES = {
46255     'load' : Roo.form.Action.Load,
46256     'submit' : Roo.form.Action.Submit
46257 };/*
46258  * Based on:
46259  * Ext JS Library 1.1.1
46260  * Copyright(c) 2006-2007, Ext JS, LLC.
46261  *
46262  * Originally Released Under LGPL - original licence link has changed is not relivant.
46263  *
46264  * Fork - LGPL
46265  * <script type="text/javascript">
46266  */
46267  
46268 /**
46269  * @class Roo.form.Layout
46270  * @extends Roo.Component
46271  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46272  * @constructor
46273  * @param {Object} config Configuration options
46274  */
46275 Roo.form.Layout = function(config){
46276     var xitems = [];
46277     if (config.items) {
46278         xitems = config.items;
46279         delete config.items;
46280     }
46281     Roo.form.Layout.superclass.constructor.call(this, config);
46282     this.stack = [];
46283     Roo.each(xitems, this.addxtype, this);
46284      
46285 };
46286
46287 Roo.extend(Roo.form.Layout, Roo.Component, {
46288     /**
46289      * @cfg {String/Object} autoCreate
46290      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46291      */
46292     /**
46293      * @cfg {String/Object/Function} style
46294      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46295      * a function which returns such a specification.
46296      */
46297     /**
46298      * @cfg {String} labelAlign
46299      * Valid values are "left," "top" and "right" (defaults to "left")
46300      */
46301     /**
46302      * @cfg {Number} labelWidth
46303      * Fixed width in pixels of all field labels (defaults to undefined)
46304      */
46305     /**
46306      * @cfg {Boolean} clear
46307      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46308      */
46309     clear : true,
46310     /**
46311      * @cfg {String} labelSeparator
46312      * The separator to use after field labels (defaults to ':')
46313      */
46314     labelSeparator : ':',
46315     /**
46316      * @cfg {Boolean} hideLabels
46317      * True to suppress the display of field labels in this layout (defaults to false)
46318      */
46319     hideLabels : false,
46320
46321     // private
46322     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46323     
46324     isLayout : true,
46325     
46326     // private
46327     onRender : function(ct, position){
46328         if(this.el){ // from markup
46329             this.el = Roo.get(this.el);
46330         }else {  // generate
46331             var cfg = this.getAutoCreate();
46332             this.el = ct.createChild(cfg, position);
46333         }
46334         if(this.style){
46335             this.el.applyStyles(this.style);
46336         }
46337         if(this.labelAlign){
46338             this.el.addClass('x-form-label-'+this.labelAlign);
46339         }
46340         if(this.hideLabels){
46341             this.labelStyle = "display:none";
46342             this.elementStyle = "padding-left:0;";
46343         }else{
46344             if(typeof this.labelWidth == 'number'){
46345                 this.labelStyle = "width:"+this.labelWidth+"px;";
46346                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46347             }
46348             if(this.labelAlign == 'top'){
46349                 this.labelStyle = "width:auto;";
46350                 this.elementStyle = "padding-left:0;";
46351             }
46352         }
46353         var stack = this.stack;
46354         var slen = stack.length;
46355         if(slen > 0){
46356             if(!this.fieldTpl){
46357                 var t = new Roo.Template(
46358                     '<div class="x-form-item {5}">',
46359                         '<label for="{0}" style="{2}">{1}{4}</label>',
46360                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46361                         '</div>',
46362                     '</div><div class="x-form-clear-left"></div>'
46363                 );
46364                 t.disableFormats = true;
46365                 t.compile();
46366                 Roo.form.Layout.prototype.fieldTpl = t;
46367             }
46368             for(var i = 0; i < slen; i++) {
46369                 if(stack[i].isFormField){
46370                     this.renderField(stack[i]);
46371                 }else{
46372                     this.renderComponent(stack[i]);
46373                 }
46374             }
46375         }
46376         if(this.clear){
46377             this.el.createChild({cls:'x-form-clear'});
46378         }
46379     },
46380
46381     // private
46382     renderField : function(f){
46383         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46384                f.id, //0
46385                f.fieldLabel, //1
46386                f.labelStyle||this.labelStyle||'', //2
46387                this.elementStyle||'', //3
46388                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46389                f.itemCls||this.itemCls||''  //5
46390        ], true).getPrevSibling());
46391     },
46392
46393     // private
46394     renderComponent : function(c){
46395         c.render(c.isLayout ? this.el : this.el.createChild());    
46396     },
46397     /**
46398      * Adds a object form elements (using the xtype property as the factory method.)
46399      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46400      * @param {Object} config 
46401      */
46402     addxtype : function(o)
46403     {
46404         // create the lement.
46405         o.form = this.form;
46406         var fe = Roo.factory(o, Roo.form);
46407         this.form.allItems.push(fe);
46408         this.stack.push(fe);
46409         
46410         if (fe.isFormField) {
46411             this.form.items.add(fe);
46412         }
46413          
46414         return fe;
46415     }
46416 });
46417
46418 /**
46419  * @class Roo.form.Column
46420  * @extends Roo.form.Layout
46421  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46422  * @constructor
46423  * @param {Object} config Configuration options
46424  */
46425 Roo.form.Column = function(config){
46426     Roo.form.Column.superclass.constructor.call(this, config);
46427 };
46428
46429 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46430     /**
46431      * @cfg {Number/String} width
46432      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46433      */
46434     /**
46435      * @cfg {String/Object} autoCreate
46436      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46437      */
46438
46439     // private
46440     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46441
46442     // private
46443     onRender : function(ct, position){
46444         Roo.form.Column.superclass.onRender.call(this, ct, position);
46445         if(this.width){
46446             this.el.setWidth(this.width);
46447         }
46448     }
46449 });
46450
46451
46452 /**
46453  * @class Roo.form.Row
46454  * @extends Roo.form.Layout
46455  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46456  * @constructor
46457  * @param {Object} config Configuration options
46458  */
46459
46460  
46461 Roo.form.Row = function(config){
46462     Roo.form.Row.superclass.constructor.call(this, config);
46463 };
46464  
46465 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46466       /**
46467      * @cfg {Number/String} width
46468      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46469      */
46470     /**
46471      * @cfg {Number/String} height
46472      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46473      */
46474     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46475     
46476     padWidth : 20,
46477     // private
46478     onRender : function(ct, position){
46479         //console.log('row render');
46480         if(!this.rowTpl){
46481             var t = new Roo.Template(
46482                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46483                     '<label for="{0}" style="{2}">{1}{4}</label>',
46484                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46485                     '</div>',
46486                 '</div>'
46487             );
46488             t.disableFormats = true;
46489             t.compile();
46490             Roo.form.Layout.prototype.rowTpl = t;
46491         }
46492         this.fieldTpl = this.rowTpl;
46493         
46494         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46495         var labelWidth = 100;
46496         
46497         if ((this.labelAlign != 'top')) {
46498             if (typeof this.labelWidth == 'number') {
46499                 labelWidth = this.labelWidth
46500             }
46501             this.padWidth =  20 + labelWidth;
46502             
46503         }
46504         
46505         Roo.form.Column.superclass.onRender.call(this, ct, position);
46506         if(this.width){
46507             this.el.setWidth(this.width);
46508         }
46509         if(this.height){
46510             this.el.setHeight(this.height);
46511         }
46512     },
46513     
46514     // private
46515     renderField : function(f){
46516         f.fieldEl = this.fieldTpl.append(this.el, [
46517                f.id, f.fieldLabel,
46518                f.labelStyle||this.labelStyle||'',
46519                this.elementStyle||'',
46520                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46521                f.itemCls||this.itemCls||'',
46522                f.width ? f.width + this.padWidth : 160 + this.padWidth
46523        ],true);
46524     }
46525 });
46526  
46527
46528 /**
46529  * @class Roo.form.FieldSet
46530  * @extends Roo.form.Layout
46531  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46532  * @constructor
46533  * @param {Object} config Configuration options
46534  */
46535 Roo.form.FieldSet = function(config){
46536     Roo.form.FieldSet.superclass.constructor.call(this, config);
46537 };
46538
46539 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46540     /**
46541      * @cfg {String} legend
46542      * The text to display as the legend for the FieldSet (defaults to '')
46543      */
46544     /**
46545      * @cfg {String/Object} autoCreate
46546      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46547      */
46548
46549     // private
46550     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46551
46552     // private
46553     onRender : function(ct, position){
46554         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46555         if(this.legend){
46556             this.setLegend(this.legend);
46557         }
46558     },
46559
46560     // private
46561     setLegend : function(text){
46562         if(this.rendered){
46563             this.el.child('legend').update(text);
46564         }
46565     }
46566 });/*
46567  * Based on:
46568  * Ext JS Library 1.1.1
46569  * Copyright(c) 2006-2007, Ext JS, LLC.
46570  *
46571  * Originally Released Under LGPL - original licence link has changed is not relivant.
46572  *
46573  * Fork - LGPL
46574  * <script type="text/javascript">
46575  */
46576 /**
46577  * @class Roo.form.VTypes
46578  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46579  * @singleton
46580  */
46581 Roo.form.VTypes = function(){
46582     // closure these in so they are only created once.
46583     var alpha = /^[a-zA-Z_]+$/;
46584     var alphanum = /^[a-zA-Z0-9_]+$/;
46585     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46586     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46587
46588     // All these messages and functions are configurable
46589     return {
46590         /**
46591          * The function used to validate email addresses
46592          * @param {String} value The email address
46593          */
46594         'email' : function(v){
46595             return email.test(v);
46596         },
46597         /**
46598          * The error text to display when the email validation function returns false
46599          * @type String
46600          */
46601         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46602         /**
46603          * The keystroke filter mask to be applied on email input
46604          * @type RegExp
46605          */
46606         'emailMask' : /[a-z0-9_\.\-@]/i,
46607
46608         /**
46609          * The function used to validate URLs
46610          * @param {String} value The URL
46611          */
46612         'url' : function(v){
46613             return url.test(v);
46614         },
46615         /**
46616          * The error text to display when the url validation function returns false
46617          * @type String
46618          */
46619         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46620         
46621         /**
46622          * The function used to validate alpha values
46623          * @param {String} value The value
46624          */
46625         'alpha' : function(v){
46626             return alpha.test(v);
46627         },
46628         /**
46629          * The error text to display when the alpha validation function returns false
46630          * @type String
46631          */
46632         'alphaText' : 'This field should only contain letters and _',
46633         /**
46634          * The keystroke filter mask to be applied on alpha input
46635          * @type RegExp
46636          */
46637         'alphaMask' : /[a-z_]/i,
46638
46639         /**
46640          * The function used to validate alphanumeric values
46641          * @param {String} value The value
46642          */
46643         'alphanum' : function(v){
46644             return alphanum.test(v);
46645         },
46646         /**
46647          * The error text to display when the alphanumeric validation function returns false
46648          * @type String
46649          */
46650         'alphanumText' : 'This field should only contain letters, numbers and _',
46651         /**
46652          * The keystroke filter mask to be applied on alphanumeric input
46653          * @type RegExp
46654          */
46655         'alphanumMask' : /[a-z0-9_]/i
46656     };
46657 }();//<script type="text/javascript">
46658
46659 /**
46660  * @class Roo.form.FCKeditor
46661  * @extends Roo.form.TextArea
46662  * Wrapper around the FCKEditor http://www.fckeditor.net
46663  * @constructor
46664  * Creates a new FCKeditor
46665  * @param {Object} config Configuration options
46666  */
46667 Roo.form.FCKeditor = function(config){
46668     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46669     this.addEvents({
46670          /**
46671          * @event editorinit
46672          * Fired when the editor is initialized - you can add extra handlers here..
46673          * @param {FCKeditor} this
46674          * @param {Object} the FCK object.
46675          */
46676         editorinit : true
46677     });
46678     
46679     
46680 };
46681 Roo.form.FCKeditor.editors = { };
46682 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46683 {
46684     //defaultAutoCreate : {
46685     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46686     //},
46687     // private
46688     /**
46689      * @cfg {Object} fck options - see fck manual for details.
46690      */
46691     fckconfig : false,
46692     
46693     /**
46694      * @cfg {Object} fck toolbar set (Basic or Default)
46695      */
46696     toolbarSet : 'Basic',
46697     /**
46698      * @cfg {Object} fck BasePath
46699      */ 
46700     basePath : '/fckeditor/',
46701     
46702     
46703     frame : false,
46704     
46705     value : '',
46706     
46707    
46708     onRender : function(ct, position)
46709     {
46710         if(!this.el){
46711             this.defaultAutoCreate = {
46712                 tag: "textarea",
46713                 style:"width:300px;height:60px;",
46714                 autocomplete: "new-password"
46715             };
46716         }
46717         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46718         /*
46719         if(this.grow){
46720             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46721             if(this.preventScrollbars){
46722                 this.el.setStyle("overflow", "hidden");
46723             }
46724             this.el.setHeight(this.growMin);
46725         }
46726         */
46727         //console.log('onrender' + this.getId() );
46728         Roo.form.FCKeditor.editors[this.getId()] = this;
46729          
46730
46731         this.replaceTextarea() ;
46732         
46733     },
46734     
46735     getEditor : function() {
46736         return this.fckEditor;
46737     },
46738     /**
46739      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46740      * @param {Mixed} value The value to set
46741      */
46742     
46743     
46744     setValue : function(value)
46745     {
46746         //console.log('setValue: ' + value);
46747         
46748         if(typeof(value) == 'undefined') { // not sure why this is happending...
46749             return;
46750         }
46751         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46752         
46753         //if(!this.el || !this.getEditor()) {
46754         //    this.value = value;
46755             //this.setValue.defer(100,this,[value]);    
46756         //    return;
46757         //} 
46758         
46759         if(!this.getEditor()) {
46760             return;
46761         }
46762         
46763         this.getEditor().SetData(value);
46764         
46765         //
46766
46767     },
46768
46769     /**
46770      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46771      * @return {Mixed} value The field value
46772      */
46773     getValue : function()
46774     {
46775         
46776         if (this.frame && this.frame.dom.style.display == 'none') {
46777             return Roo.form.FCKeditor.superclass.getValue.call(this);
46778         }
46779         
46780         if(!this.el || !this.getEditor()) {
46781            
46782            // this.getValue.defer(100,this); 
46783             return this.value;
46784         }
46785        
46786         
46787         var value=this.getEditor().GetData();
46788         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46789         return Roo.form.FCKeditor.superclass.getValue.call(this);
46790         
46791
46792     },
46793
46794     /**
46795      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46796      * @return {Mixed} value The field value
46797      */
46798     getRawValue : function()
46799     {
46800         if (this.frame && this.frame.dom.style.display == 'none') {
46801             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46802         }
46803         
46804         if(!this.el || !this.getEditor()) {
46805             //this.getRawValue.defer(100,this); 
46806             return this.value;
46807             return;
46808         }
46809         
46810         
46811         
46812         var value=this.getEditor().GetData();
46813         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46814         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46815          
46816     },
46817     
46818     setSize : function(w,h) {
46819         
46820         
46821         
46822         //if (this.frame && this.frame.dom.style.display == 'none') {
46823         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46824         //    return;
46825         //}
46826         //if(!this.el || !this.getEditor()) {
46827         //    this.setSize.defer(100,this, [w,h]); 
46828         //    return;
46829         //}
46830         
46831         
46832         
46833         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46834         
46835         this.frame.dom.setAttribute('width', w);
46836         this.frame.dom.setAttribute('height', h);
46837         this.frame.setSize(w,h);
46838         
46839     },
46840     
46841     toggleSourceEdit : function(value) {
46842         
46843       
46844          
46845         this.el.dom.style.display = value ? '' : 'none';
46846         this.frame.dom.style.display = value ?  'none' : '';
46847         
46848     },
46849     
46850     
46851     focus: function(tag)
46852     {
46853         if (this.frame.dom.style.display == 'none') {
46854             return Roo.form.FCKeditor.superclass.focus.call(this);
46855         }
46856         if(!this.el || !this.getEditor()) {
46857             this.focus.defer(100,this, [tag]); 
46858             return;
46859         }
46860         
46861         
46862         
46863         
46864         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46865         this.getEditor().Focus();
46866         if (tgs.length) {
46867             if (!this.getEditor().Selection.GetSelection()) {
46868                 this.focus.defer(100,this, [tag]); 
46869                 return;
46870             }
46871             
46872             
46873             var r = this.getEditor().EditorDocument.createRange();
46874             r.setStart(tgs[0],0);
46875             r.setEnd(tgs[0],0);
46876             this.getEditor().Selection.GetSelection().removeAllRanges();
46877             this.getEditor().Selection.GetSelection().addRange(r);
46878             this.getEditor().Focus();
46879         }
46880         
46881     },
46882     
46883     
46884     
46885     replaceTextarea : function()
46886     {
46887         if ( document.getElementById( this.getId() + '___Frame' ) )
46888             return ;
46889         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46890         //{
46891             // We must check the elements firstly using the Id and then the name.
46892         var oTextarea = document.getElementById( this.getId() );
46893         
46894         var colElementsByName = document.getElementsByName( this.getId() ) ;
46895          
46896         oTextarea.style.display = 'none' ;
46897
46898         if ( oTextarea.tabIndex ) {            
46899             this.TabIndex = oTextarea.tabIndex ;
46900         }
46901         
46902         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46903         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46904         this.frame = Roo.get(this.getId() + '___Frame')
46905     },
46906     
46907     _getConfigHtml : function()
46908     {
46909         var sConfig = '' ;
46910
46911         for ( var o in this.fckconfig ) {
46912             sConfig += sConfig.length > 0  ? '&amp;' : '';
46913             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46914         }
46915
46916         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46917     },
46918     
46919     
46920     _getIFrameHtml : function()
46921     {
46922         var sFile = 'fckeditor.html' ;
46923         /* no idea what this is about..
46924         try
46925         {
46926             if ( (/fcksource=true/i).test( window.top.location.search ) )
46927                 sFile = 'fckeditor.original.html' ;
46928         }
46929         catch (e) { 
46930         */
46931
46932         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46933         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46934         
46935         
46936         var html = '<iframe id="' + this.getId() +
46937             '___Frame" src="' + sLink +
46938             '" width="' + this.width +
46939             '" height="' + this.height + '"' +
46940             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46941             ' frameborder="0" scrolling="no"></iframe>' ;
46942
46943         return html ;
46944     },
46945     
46946     _insertHtmlBefore : function( html, element )
46947     {
46948         if ( element.insertAdjacentHTML )       {
46949             // IE
46950             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46951         } else { // Gecko
46952             var oRange = document.createRange() ;
46953             oRange.setStartBefore( element ) ;
46954             var oFragment = oRange.createContextualFragment( html );
46955             element.parentNode.insertBefore( oFragment, element ) ;
46956         }
46957     }
46958     
46959     
46960   
46961     
46962     
46963     
46964     
46965
46966 });
46967
46968 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46969
46970 function FCKeditor_OnComplete(editorInstance){
46971     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46972     f.fckEditor = editorInstance;
46973     //console.log("loaded");
46974     f.fireEvent('editorinit', f, editorInstance);
46975
46976   
46977
46978  
46979
46980
46981
46982
46983
46984
46985
46986
46987
46988
46989
46990
46991
46992
46993
46994 //<script type="text/javascript">
46995 /**
46996  * @class Roo.form.GridField
46997  * @extends Roo.form.Field
46998  * Embed a grid (or editable grid into a form)
46999  * STATUS ALPHA
47000  * 
47001  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47002  * it needs 
47003  * xgrid.store = Roo.data.Store
47004  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47005  * xgrid.store.reader = Roo.data.JsonReader 
47006  * 
47007  * 
47008  * @constructor
47009  * Creates a new GridField
47010  * @param {Object} config Configuration options
47011  */
47012 Roo.form.GridField = function(config){
47013     Roo.form.GridField.superclass.constructor.call(this, config);
47014      
47015 };
47016
47017 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47018     /**
47019      * @cfg {Number} width  - used to restrict width of grid..
47020      */
47021     width : 100,
47022     /**
47023      * @cfg {Number} height - used to restrict height of grid..
47024      */
47025     height : 50,
47026      /**
47027      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47028          * 
47029          *}
47030      */
47031     xgrid : false, 
47032     /**
47033      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47034      * {tag: "input", type: "checkbox", autocomplete: "off"})
47035      */
47036    // defaultAutoCreate : { tag: 'div' },
47037     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47038     /**
47039      * @cfg {String} addTitle Text to include for adding a title.
47040      */
47041     addTitle : false,
47042     //
47043     onResize : function(){
47044         Roo.form.Field.superclass.onResize.apply(this, arguments);
47045     },
47046
47047     initEvents : function(){
47048         // Roo.form.Checkbox.superclass.initEvents.call(this);
47049         // has no events...
47050        
47051     },
47052
47053
47054     getResizeEl : function(){
47055         return this.wrap;
47056     },
47057
47058     getPositionEl : function(){
47059         return this.wrap;
47060     },
47061
47062     // private
47063     onRender : function(ct, position){
47064         
47065         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47066         var style = this.style;
47067         delete this.style;
47068         
47069         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47070         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47071         this.viewEl = this.wrap.createChild({ tag: 'div' });
47072         if (style) {
47073             this.viewEl.applyStyles(style);
47074         }
47075         if (this.width) {
47076             this.viewEl.setWidth(this.width);
47077         }
47078         if (this.height) {
47079             this.viewEl.setHeight(this.height);
47080         }
47081         //if(this.inputValue !== undefined){
47082         //this.setValue(this.value);
47083         
47084         
47085         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47086         
47087         
47088         this.grid.render();
47089         this.grid.getDataSource().on('remove', this.refreshValue, this);
47090         this.grid.getDataSource().on('update', this.refreshValue, this);
47091         this.grid.on('afteredit', this.refreshValue, this);
47092  
47093     },
47094      
47095     
47096     /**
47097      * Sets the value of the item. 
47098      * @param {String} either an object  or a string..
47099      */
47100     setValue : function(v){
47101         //this.value = v;
47102         v = v || []; // empty set..
47103         // this does not seem smart - it really only affects memoryproxy grids..
47104         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47105             var ds = this.grid.getDataSource();
47106             // assumes a json reader..
47107             var data = {}
47108             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47109             ds.loadData( data);
47110         }
47111         // clear selection so it does not get stale.
47112         if (this.grid.sm) { 
47113             this.grid.sm.clearSelections();
47114         }
47115         
47116         Roo.form.GridField.superclass.setValue.call(this, v);
47117         this.refreshValue();
47118         // should load data in the grid really....
47119     },
47120     
47121     // private
47122     refreshValue: function() {
47123          var val = [];
47124         this.grid.getDataSource().each(function(r) {
47125             val.push(r.data);
47126         });
47127         this.el.dom.value = Roo.encode(val);
47128     }
47129     
47130      
47131     
47132     
47133 });/*
47134  * Based on:
47135  * Ext JS Library 1.1.1
47136  * Copyright(c) 2006-2007, Ext JS, LLC.
47137  *
47138  * Originally Released Under LGPL - original licence link has changed is not relivant.
47139  *
47140  * Fork - LGPL
47141  * <script type="text/javascript">
47142  */
47143 /**
47144  * @class Roo.form.DisplayField
47145  * @extends Roo.form.Field
47146  * A generic Field to display non-editable data.
47147  * @constructor
47148  * Creates a new Display Field item.
47149  * @param {Object} config Configuration options
47150  */
47151 Roo.form.DisplayField = function(config){
47152     Roo.form.DisplayField.superclass.constructor.call(this, config);
47153     
47154 };
47155
47156 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47157     inputType:      'hidden',
47158     allowBlank:     true,
47159     readOnly:         true,
47160     
47161  
47162     /**
47163      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47164      */
47165     focusClass : undefined,
47166     /**
47167      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47168      */
47169     fieldClass: 'x-form-field',
47170     
47171      /**
47172      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47173      */
47174     valueRenderer: undefined,
47175     
47176     width: 100,
47177     /**
47178      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47179      * {tag: "input", type: "checkbox", autocomplete: "off"})
47180      */
47181      
47182  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47183
47184     onResize : function(){
47185         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47186         
47187     },
47188
47189     initEvents : function(){
47190         // Roo.form.Checkbox.superclass.initEvents.call(this);
47191         // has no events...
47192        
47193     },
47194
47195
47196     getResizeEl : function(){
47197         return this.wrap;
47198     },
47199
47200     getPositionEl : function(){
47201         return this.wrap;
47202     },
47203
47204     // private
47205     onRender : function(ct, position){
47206         
47207         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47208         //if(this.inputValue !== undefined){
47209         this.wrap = this.el.wrap();
47210         
47211         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47212         
47213         if (this.bodyStyle) {
47214             this.viewEl.applyStyles(this.bodyStyle);
47215         }
47216         //this.viewEl.setStyle('padding', '2px');
47217         
47218         this.setValue(this.value);
47219         
47220     },
47221 /*
47222     // private
47223     initValue : Roo.emptyFn,
47224
47225   */
47226
47227         // private
47228     onClick : function(){
47229         
47230     },
47231
47232     /**
47233      * Sets the checked state of the checkbox.
47234      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47235      */
47236     setValue : function(v){
47237         this.value = v;
47238         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47239         // this might be called before we have a dom element..
47240         if (!this.viewEl) {
47241             return;
47242         }
47243         this.viewEl.dom.innerHTML = html;
47244         Roo.form.DisplayField.superclass.setValue.call(this, v);
47245
47246     }
47247 });/*
47248  * 
47249  * Licence- LGPL
47250  * 
47251  */
47252
47253 /**
47254  * @class Roo.form.DayPicker
47255  * @extends Roo.form.Field
47256  * A Day picker show [M] [T] [W] ....
47257  * @constructor
47258  * Creates a new Day Picker
47259  * @param {Object} config Configuration options
47260  */
47261 Roo.form.DayPicker= function(config){
47262     Roo.form.DayPicker.superclass.constructor.call(this, config);
47263      
47264 };
47265
47266 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47267     /**
47268      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47269      */
47270     focusClass : undefined,
47271     /**
47272      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47273      */
47274     fieldClass: "x-form-field",
47275    
47276     /**
47277      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47278      * {tag: "input", type: "checkbox", autocomplete: "off"})
47279      */
47280     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47281     
47282    
47283     actionMode : 'viewEl', 
47284     //
47285     // private
47286  
47287     inputType : 'hidden',
47288     
47289      
47290     inputElement: false, // real input element?
47291     basedOn: false, // ????
47292     
47293     isFormField: true, // not sure where this is needed!!!!
47294
47295     onResize : function(){
47296         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47297         if(!this.boxLabel){
47298             this.el.alignTo(this.wrap, 'c-c');
47299         }
47300     },
47301
47302     initEvents : function(){
47303         Roo.form.Checkbox.superclass.initEvents.call(this);
47304         this.el.on("click", this.onClick,  this);
47305         this.el.on("change", this.onClick,  this);
47306     },
47307
47308
47309     getResizeEl : function(){
47310         return this.wrap;
47311     },
47312
47313     getPositionEl : function(){
47314         return this.wrap;
47315     },
47316
47317     
47318     // private
47319     onRender : function(ct, position){
47320         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47321        
47322         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47323         
47324         var r1 = '<table><tr>';
47325         var r2 = '<tr class="x-form-daypick-icons">';
47326         for (var i=0; i < 7; i++) {
47327             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47328             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47329         }
47330         
47331         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47332         viewEl.select('img').on('click', this.onClick, this);
47333         this.viewEl = viewEl;   
47334         
47335         
47336         // this will not work on Chrome!!!
47337         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47338         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47339         
47340         
47341           
47342
47343     },
47344
47345     // private
47346     initValue : Roo.emptyFn,
47347
47348     /**
47349      * Returns the checked state of the checkbox.
47350      * @return {Boolean} True if checked, else false
47351      */
47352     getValue : function(){
47353         return this.el.dom.value;
47354         
47355     },
47356
47357         // private
47358     onClick : function(e){ 
47359         //this.setChecked(!this.checked);
47360         Roo.get(e.target).toggleClass('x-menu-item-checked');
47361         this.refreshValue();
47362         //if(this.el.dom.checked != this.checked){
47363         //    this.setValue(this.el.dom.checked);
47364        // }
47365     },
47366     
47367     // private
47368     refreshValue : function()
47369     {
47370         var val = '';
47371         this.viewEl.select('img',true).each(function(e,i,n)  {
47372             val += e.is(".x-menu-item-checked") ? String(n) : '';
47373         });
47374         this.setValue(val, true);
47375     },
47376
47377     /**
47378      * Sets the checked state of the checkbox.
47379      * On is always based on a string comparison between inputValue and the param.
47380      * @param {Boolean/String} value - the value to set 
47381      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47382      */
47383     setValue : function(v,suppressEvent){
47384         if (!this.el.dom) {
47385             return;
47386         }
47387         var old = this.el.dom.value ;
47388         this.el.dom.value = v;
47389         if (suppressEvent) {
47390             return ;
47391         }
47392          
47393         // update display..
47394         this.viewEl.select('img',true).each(function(e,i,n)  {
47395             
47396             var on = e.is(".x-menu-item-checked");
47397             var newv = v.indexOf(String(n)) > -1;
47398             if (on != newv) {
47399                 e.toggleClass('x-menu-item-checked');
47400             }
47401             
47402         });
47403         
47404         
47405         this.fireEvent('change', this, v, old);
47406         
47407         
47408     },
47409    
47410     // handle setting of hidden value by some other method!!?!?
47411     setFromHidden: function()
47412     {
47413         if(!this.el){
47414             return;
47415         }
47416         //console.log("SET FROM HIDDEN");
47417         //alert('setFrom hidden');
47418         this.setValue(this.el.dom.value);
47419     },
47420     
47421     onDestroy : function()
47422     {
47423         if(this.viewEl){
47424             Roo.get(this.viewEl).remove();
47425         }
47426          
47427         Roo.form.DayPicker.superclass.onDestroy.call(this);
47428     }
47429
47430 });/*
47431  * RooJS Library 1.1.1
47432  * Copyright(c) 2008-2011  Alan Knowles
47433  *
47434  * License - LGPL
47435  */
47436  
47437
47438 /**
47439  * @class Roo.form.ComboCheck
47440  * @extends Roo.form.ComboBox
47441  * A combobox for multiple select items.
47442  *
47443  * FIXME - could do with a reset button..
47444  * 
47445  * @constructor
47446  * Create a new ComboCheck
47447  * @param {Object} config Configuration options
47448  */
47449 Roo.form.ComboCheck = function(config){
47450     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47451     // should verify some data...
47452     // like
47453     // hiddenName = required..
47454     // displayField = required
47455     // valudField == required
47456     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47457     var _t = this;
47458     Roo.each(req, function(e) {
47459         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47460             throw "Roo.form.ComboCheck : missing value for: " + e;
47461         }
47462     });
47463     
47464     
47465 };
47466
47467 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47468      
47469      
47470     editable : false,
47471      
47472     selectedClass: 'x-menu-item-checked', 
47473     
47474     // private
47475     onRender : function(ct, position){
47476         var _t = this;
47477         
47478         
47479         
47480         if(!this.tpl){
47481             var cls = 'x-combo-list';
47482
47483             
47484             this.tpl =  new Roo.Template({
47485                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47486                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47487                    '<span>{' + this.displayField + '}</span>' +
47488                     '</div>' 
47489                 
47490             });
47491         }
47492  
47493         
47494         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47495         this.view.singleSelect = false;
47496         this.view.multiSelect = true;
47497         this.view.toggleSelect = true;
47498         this.pageTb.add(new Roo.Toolbar.Fill(), {
47499             
47500             text: 'Done',
47501             handler: function()
47502             {
47503                 _t.collapse();
47504             }
47505         });
47506     },
47507     
47508     onViewOver : function(e, t){
47509         // do nothing...
47510         return;
47511         
47512     },
47513     
47514     onViewClick : function(doFocus,index){
47515         return;
47516         
47517     },
47518     select: function () {
47519         //Roo.log("SELECT CALLED");
47520     },
47521      
47522     selectByValue : function(xv, scrollIntoView){
47523         var ar = this.getValueArray();
47524         var sels = [];
47525         
47526         Roo.each(ar, function(v) {
47527             if(v === undefined || v === null){
47528                 return;
47529             }
47530             var r = this.findRecord(this.valueField, v);
47531             if(r){
47532                 sels.push(this.store.indexOf(r))
47533                 
47534             }
47535         },this);
47536         this.view.select(sels);
47537         return false;
47538     },
47539     
47540     
47541     
47542     onSelect : function(record, index){
47543        // Roo.log("onselect Called");
47544        // this is only called by the clear button now..
47545         this.view.clearSelections();
47546         this.setValue('[]');
47547         if (this.value != this.valueBefore) {
47548             this.fireEvent('change', this, this.value, this.valueBefore);
47549             this.valueBefore = this.value;
47550         }
47551     },
47552     getValueArray : function()
47553     {
47554         var ar = [] ;
47555         
47556         try {
47557             //Roo.log(this.value);
47558             if (typeof(this.value) == 'undefined') {
47559                 return [];
47560             }
47561             var ar = Roo.decode(this.value);
47562             return  ar instanceof Array ? ar : []; //?? valid?
47563             
47564         } catch(e) {
47565             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47566             return [];
47567         }
47568          
47569     },
47570     expand : function ()
47571     {
47572         
47573         Roo.form.ComboCheck.superclass.expand.call(this);
47574         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47575         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47576         
47577
47578     },
47579     
47580     collapse : function(){
47581         Roo.form.ComboCheck.superclass.collapse.call(this);
47582         var sl = this.view.getSelectedIndexes();
47583         var st = this.store;
47584         var nv = [];
47585         var tv = [];
47586         var r;
47587         Roo.each(sl, function(i) {
47588             r = st.getAt(i);
47589             nv.push(r.get(this.valueField));
47590         },this);
47591         this.setValue(Roo.encode(nv));
47592         if (this.value != this.valueBefore) {
47593
47594             this.fireEvent('change', this, this.value, this.valueBefore);
47595             this.valueBefore = this.value;
47596         }
47597         
47598     },
47599     
47600     setValue : function(v){
47601         // Roo.log(v);
47602         this.value = v;
47603         
47604         var vals = this.getValueArray();
47605         var tv = [];
47606         Roo.each(vals, function(k) {
47607             var r = this.findRecord(this.valueField, k);
47608             if(r){
47609                 tv.push(r.data[this.displayField]);
47610             }else if(this.valueNotFoundText !== undefined){
47611                 tv.push( this.valueNotFoundText );
47612             }
47613         },this);
47614        // Roo.log(tv);
47615         
47616         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47617         this.hiddenField.value = v;
47618         this.value = v;
47619     }
47620     
47621 });/*
47622  * Based on:
47623  * Ext JS Library 1.1.1
47624  * Copyright(c) 2006-2007, Ext JS, LLC.
47625  *
47626  * Originally Released Under LGPL - original licence link has changed is not relivant.
47627  *
47628  * Fork - LGPL
47629  * <script type="text/javascript">
47630  */
47631  
47632 /**
47633  * @class Roo.form.Signature
47634  * @extends Roo.form.Field
47635  * Signature field.  
47636  * @constructor
47637  * 
47638  * @param {Object} config Configuration options
47639  */
47640
47641 Roo.form.Signature = function(config){
47642     Roo.form.Signature.superclass.constructor.call(this, config);
47643     
47644     this.addEvents({// not in used??
47645          /**
47646          * @event confirm
47647          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47648              * @param {Roo.form.Signature} combo This combo box
47649              */
47650         'confirm' : true,
47651         /**
47652          * @event reset
47653          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47654              * @param {Roo.form.ComboBox} combo This combo box
47655              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47656              */
47657         'reset' : true
47658     });
47659 };
47660
47661 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47662     /**
47663      * @cfg {Object} labels Label to use when rendering a form.
47664      * defaults to 
47665      * labels : { 
47666      *      clear : "Clear",
47667      *      confirm : "Confirm"
47668      *  }
47669      */
47670     labels : { 
47671         clear : "Clear",
47672         confirm : "Confirm"
47673     },
47674     /**
47675      * @cfg {Number} width The signature panel width (defaults to 300)
47676      */
47677     width: 300,
47678     /**
47679      * @cfg {Number} height The signature panel height (defaults to 100)
47680      */
47681     height : 100,
47682     /**
47683      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47684      */
47685     allowBlank : false,
47686     
47687     //private
47688     // {Object} signPanel The signature SVG panel element (defaults to {})
47689     signPanel : {},
47690     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47691     isMouseDown : false,
47692     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47693     isConfirmed : false,
47694     // {String} signatureTmp SVG mapping string (defaults to empty string)
47695     signatureTmp : '',
47696     
47697     
47698     defaultAutoCreate : { // modified by initCompnoent..
47699         tag: "input",
47700         type:"hidden"
47701     },
47702
47703     // private
47704     onRender : function(ct, position){
47705         
47706         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47707         
47708         this.wrap = this.el.wrap({
47709             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47710         });
47711         
47712         this.createToolbar(this);
47713         this.signPanel = this.wrap.createChild({
47714                 tag: 'div',
47715                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47716             }, this.el
47717         );
47718             
47719         this.svgID = Roo.id();
47720         this.svgEl = this.signPanel.createChild({
47721               xmlns : 'http://www.w3.org/2000/svg',
47722               tag : 'svg',
47723               id : this.svgID + "-svg",
47724               width: this.width,
47725               height: this.height,
47726               viewBox: '0 0 '+this.width+' '+this.height,
47727               cn : [
47728                 {
47729                     tag: "rect",
47730                     id: this.svgID + "-svg-r",
47731                     width: this.width,
47732                     height: this.height,
47733                     fill: "#ffa"
47734                 },
47735                 {
47736                     tag: "line",
47737                     id: this.svgID + "-svg-l",
47738                     x1: "0", // start
47739                     y1: (this.height*0.8), // start set the line in 80% of height
47740                     x2: this.width, // end
47741                     y2: (this.height*0.8), // end set the line in 80% of height
47742                     'stroke': "#666",
47743                     'stroke-width': "1",
47744                     'stroke-dasharray': "3",
47745                     'shape-rendering': "crispEdges",
47746                     'pointer-events': "none"
47747                 },
47748                 {
47749                     tag: "path",
47750                     id: this.svgID + "-svg-p",
47751                     'stroke': "navy",
47752                     'stroke-width': "3",
47753                     'fill': "none",
47754                     'pointer-events': 'none'
47755                 }
47756               ]
47757         });
47758         this.createSVG();
47759         this.svgBox = this.svgEl.dom.getScreenCTM();
47760     },
47761     createSVG : function(){ 
47762         var svg = this.signPanel;
47763         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47764         var t = this;
47765
47766         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47767         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47768         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47769         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47770         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47771         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47772         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47773         
47774     },
47775     isTouchEvent : function(e){
47776         return e.type.match(/^touch/);
47777     },
47778     getCoords : function (e) {
47779         var pt    = this.svgEl.dom.createSVGPoint();
47780         pt.x = e.clientX; 
47781         pt.y = e.clientY;
47782         if (this.isTouchEvent(e)) {
47783             pt.x =  e.targetTouches[0].clientX 
47784             pt.y = e.targetTouches[0].clientY;
47785         }
47786         var a = this.svgEl.dom.getScreenCTM();
47787         var b = a.inverse();
47788         var mx = pt.matrixTransform(b);
47789         return mx.x + ',' + mx.y;
47790     },
47791     //mouse event headler 
47792     down : function (e) {
47793         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47794         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47795         
47796         this.isMouseDown = true;
47797         
47798         e.preventDefault();
47799     },
47800     move : function (e) {
47801         if (this.isMouseDown) {
47802             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47803             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47804         }
47805         
47806         e.preventDefault();
47807     },
47808     up : function (e) {
47809         this.isMouseDown = false;
47810         var sp = this.signatureTmp.split(' ');
47811         
47812         if(sp.length > 1){
47813             if(!sp[sp.length-2].match(/^L/)){
47814                 sp.pop();
47815                 sp.pop();
47816                 sp.push("");
47817                 this.signatureTmp = sp.join(" ");
47818             }
47819         }
47820         if(this.getValue() != this.signatureTmp){
47821             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47822             this.isConfirmed = false;
47823         }
47824         e.preventDefault();
47825     },
47826     
47827     /**
47828      * Protected method that will not generally be called directly. It
47829      * is called when the editor creates its toolbar. Override this method if you need to
47830      * add custom toolbar buttons.
47831      * @param {HtmlEditor} editor
47832      */
47833     createToolbar : function(editor){
47834          function btn(id, toggle, handler){
47835             var xid = fid + '-'+ id ;
47836             return {
47837                 id : xid,
47838                 cmd : id,
47839                 cls : 'x-btn-icon x-edit-'+id,
47840                 enableToggle:toggle !== false,
47841                 scope: editor, // was editor...
47842                 handler:handler||editor.relayBtnCmd,
47843                 clickEvent:'mousedown',
47844                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47845                 tabIndex:-1
47846             };
47847         }
47848         
47849         
47850         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47851         this.tb = tb;
47852         this.tb.add(
47853            {
47854                 cls : ' x-signature-btn x-signature-'+id,
47855                 scope: editor, // was editor...
47856                 handler: this.reset,
47857                 clickEvent:'mousedown',
47858                 text: this.labels.clear
47859             },
47860             {
47861                  xtype : 'Fill',
47862                  xns: Roo.Toolbar
47863             }, 
47864             {
47865                 cls : '  x-signature-btn x-signature-'+id,
47866                 scope: editor, // was editor...
47867                 handler: this.confirmHandler,
47868                 clickEvent:'mousedown',
47869                 text: this.labels.confirm
47870             }
47871         );
47872     
47873     },
47874     //public
47875     /**
47876      * when user is clicked confirm then show this image.....
47877      * 
47878      * @return {String} Image Data URI
47879      */
47880     getImageDataURI : function(){
47881         var svg = this.svgEl.dom.parentNode.innerHTML;
47882         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47883         return src; 
47884     },
47885     /**
47886      * 
47887      * @return {Boolean} this.isConfirmed
47888      */
47889     getConfirmed : function(){
47890         return this.isConfirmed;
47891     },
47892     /**
47893      * 
47894      * @return {Number} this.width
47895      */
47896     getWidth : function(){
47897         return this.width;
47898     },
47899     /**
47900      * 
47901      * @return {Number} this.height
47902      */
47903     getHeight : function(){
47904         return this.height;
47905     },
47906     // private
47907     getSignature : function(){
47908         return this.signatureTmp;
47909     },
47910     // private
47911     reset : function(){
47912         this.signatureTmp = '';
47913         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47914         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47915         this.isConfirmed = false;
47916         Roo.form.Signature.superclass.reset.call(this);
47917     },
47918     setSignature : function(s){
47919         this.signatureTmp = s;
47920         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47921         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47922         this.setValue(s);
47923         this.isConfirmed = false;
47924         Roo.form.Signature.superclass.reset.call(this);
47925     }, 
47926     test : function(){
47927 //        Roo.log(this.signPanel.dom.contentWindow.up())
47928     },
47929     //private
47930     setConfirmed : function(){
47931         
47932         
47933         
47934 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47935     },
47936     // private
47937     confirmHandler : function(){
47938         if(!this.getSignature()){
47939             return;
47940         }
47941         
47942         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47943         this.setValue(this.getSignature());
47944         this.isConfirmed = true;
47945         
47946         this.fireEvent('confirm', this);
47947     },
47948     // private
47949     // Subclasses should provide the validation implementation by overriding this
47950     validateValue : function(value){
47951         if(this.allowBlank){
47952             return true;
47953         }
47954         
47955         if(this.isConfirmed){
47956             return true;
47957         }
47958         return false;
47959     }
47960 });/*
47961  * Based on:
47962  * Ext JS Library 1.1.1
47963  * Copyright(c) 2006-2007, Ext JS, LLC.
47964  *
47965  * Originally Released Under LGPL - original licence link has changed is not relivant.
47966  *
47967  * Fork - LGPL
47968  * <script type="text/javascript">
47969  */
47970  
47971
47972 /**
47973  * @class Roo.form.ComboBox
47974  * @extends Roo.form.TriggerField
47975  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47976  * @constructor
47977  * Create a new ComboBox.
47978  * @param {Object} config Configuration options
47979  */
47980 Roo.form.Select = function(config){
47981     Roo.form.Select.superclass.constructor.call(this, config);
47982      
47983 };
47984
47985 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47986     /**
47987      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47988      */
47989     /**
47990      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47991      * rendering into an Roo.Editor, defaults to false)
47992      */
47993     /**
47994      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47995      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47996      */
47997     /**
47998      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47999      */
48000     /**
48001      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48002      * the dropdown list (defaults to undefined, with no header element)
48003      */
48004
48005      /**
48006      * @cfg {String/Roo.Template} tpl The template to use to render the output
48007      */
48008      
48009     // private
48010     defaultAutoCreate : {tag: "select"  },
48011     /**
48012      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48013      */
48014     listWidth: undefined,
48015     /**
48016      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48017      * mode = 'remote' or 'text' if mode = 'local')
48018      */
48019     displayField: undefined,
48020     /**
48021      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48022      * mode = 'remote' or 'value' if mode = 'local'). 
48023      * Note: use of a valueField requires the user make a selection
48024      * in order for a value to be mapped.
48025      */
48026     valueField: undefined,
48027     
48028     
48029     /**
48030      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48031      * field's data value (defaults to the underlying DOM element's name)
48032      */
48033     hiddenName: undefined,
48034     /**
48035      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48036      */
48037     listClass: '',
48038     /**
48039      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48040      */
48041     selectedClass: 'x-combo-selected',
48042     /**
48043      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48044      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48045      * which displays a downward arrow icon).
48046      */
48047     triggerClass : 'x-form-arrow-trigger',
48048     /**
48049      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48050      */
48051     shadow:'sides',
48052     /**
48053      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48054      * anchor positions (defaults to 'tl-bl')
48055      */
48056     listAlign: 'tl-bl?',
48057     /**
48058      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48059      */
48060     maxHeight: 300,
48061     /**
48062      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48063      * query specified by the allQuery config option (defaults to 'query')
48064      */
48065     triggerAction: 'query',
48066     /**
48067      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48068      * (defaults to 4, does not apply if editable = false)
48069      */
48070     minChars : 4,
48071     /**
48072      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48073      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48074      */
48075     typeAhead: false,
48076     /**
48077      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48078      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48079      */
48080     queryDelay: 500,
48081     /**
48082      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48083      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48084      */
48085     pageSize: 0,
48086     /**
48087      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48088      * when editable = true (defaults to false)
48089      */
48090     selectOnFocus:false,
48091     /**
48092      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48093      */
48094     queryParam: 'query',
48095     /**
48096      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48097      * when mode = 'remote' (defaults to 'Loading...')
48098      */
48099     loadingText: 'Loading...',
48100     /**
48101      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48102      */
48103     resizable: false,
48104     /**
48105      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48106      */
48107     handleHeight : 8,
48108     /**
48109      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48110      * traditional select (defaults to true)
48111      */
48112     editable: true,
48113     /**
48114      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48115      */
48116     allQuery: '',
48117     /**
48118      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48119      */
48120     mode: 'remote',
48121     /**
48122      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48123      * listWidth has a higher value)
48124      */
48125     minListWidth : 70,
48126     /**
48127      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48128      * allow the user to set arbitrary text into the field (defaults to false)
48129      */
48130     forceSelection:false,
48131     /**
48132      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48133      * if typeAhead = true (defaults to 250)
48134      */
48135     typeAheadDelay : 250,
48136     /**
48137      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48138      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48139      */
48140     valueNotFoundText : undefined,
48141     
48142     /**
48143      * @cfg {String} defaultValue The value displayed after loading the store.
48144      */
48145     defaultValue: '',
48146     
48147     /**
48148      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48149      */
48150     blockFocus : false,
48151     
48152     /**
48153      * @cfg {Boolean} disableClear Disable showing of clear button.
48154      */
48155     disableClear : false,
48156     /**
48157      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48158      */
48159     alwaysQuery : false,
48160     
48161     //private
48162     addicon : false,
48163     editicon: false,
48164     
48165     // element that contains real text value.. (when hidden is used..)
48166      
48167     // private
48168     onRender : function(ct, position){
48169         Roo.form.Field.prototype.onRender.call(this, ct, position);
48170         
48171         if(this.store){
48172             this.store.on('beforeload', this.onBeforeLoad, this);
48173             this.store.on('load', this.onLoad, this);
48174             this.store.on('loadexception', this.onLoadException, this);
48175             this.store.load({});
48176         }
48177         
48178         
48179         
48180     },
48181
48182     // private
48183     initEvents : function(){
48184         //Roo.form.ComboBox.superclass.initEvents.call(this);
48185  
48186     },
48187
48188     onDestroy : function(){
48189        
48190         if(this.store){
48191             this.store.un('beforeload', this.onBeforeLoad, this);
48192             this.store.un('load', this.onLoad, this);
48193             this.store.un('loadexception', this.onLoadException, this);
48194         }
48195         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48196     },
48197
48198     // private
48199     fireKey : function(e){
48200         if(e.isNavKeyPress() && !this.list.isVisible()){
48201             this.fireEvent("specialkey", this, e);
48202         }
48203     },
48204
48205     // private
48206     onResize: function(w, h){
48207         
48208         return; 
48209     
48210         
48211     },
48212
48213     /**
48214      * Allow or prevent the user from directly editing the field text.  If false is passed,
48215      * the user will only be able to select from the items defined in the dropdown list.  This method
48216      * is the runtime equivalent of setting the 'editable' config option at config time.
48217      * @param {Boolean} value True to allow the user to directly edit the field text
48218      */
48219     setEditable : function(value){
48220          
48221     },
48222
48223     // private
48224     onBeforeLoad : function(){
48225         
48226         Roo.log("Select before load");
48227         return;
48228     
48229         this.innerList.update(this.loadingText ?
48230                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48231         //this.restrictHeight();
48232         this.selectedIndex = -1;
48233     },
48234
48235     // private
48236     onLoad : function(){
48237
48238     
48239         var dom = this.el.dom;
48240         dom.innerHTML = '';
48241          var od = dom.ownerDocument;
48242          
48243         if (this.emptyText) {
48244             var op = od.createElement('option');
48245             op.setAttribute('value', '');
48246             op.innerHTML = String.format('{0}', this.emptyText);
48247             dom.appendChild(op);
48248         }
48249         if(this.store.getCount() > 0){
48250            
48251             var vf = this.valueField;
48252             var df = this.displayField;
48253             this.store.data.each(function(r) {
48254                 // which colmsn to use... testing - cdoe / title..
48255                 var op = od.createElement('option');
48256                 op.setAttribute('value', r.data[vf]);
48257                 op.innerHTML = String.format('{0}', r.data[df]);
48258                 dom.appendChild(op);
48259             });
48260             if (typeof(this.defaultValue != 'undefined')) {
48261                 this.setValue(this.defaultValue);
48262             }
48263             
48264              
48265         }else{
48266             //this.onEmptyResults();
48267         }
48268         //this.el.focus();
48269     },
48270     // private
48271     onLoadException : function()
48272     {
48273         dom.innerHTML = '';
48274             
48275         Roo.log("Select on load exception");
48276         return;
48277     
48278         this.collapse();
48279         Roo.log(this.store.reader.jsonData);
48280         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48281             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48282         }
48283         
48284         
48285     },
48286     // private
48287     onTypeAhead : function(){
48288          
48289     },
48290
48291     // private
48292     onSelect : function(record, index){
48293         Roo.log('on select?');
48294         return;
48295         if(this.fireEvent('beforeselect', this, record, index) !== false){
48296             this.setFromData(index > -1 ? record.data : false);
48297             this.collapse();
48298             this.fireEvent('select', this, record, index);
48299         }
48300     },
48301
48302     /**
48303      * Returns the currently selected field value or empty string if no value is set.
48304      * @return {String} value The selected value
48305      */
48306     getValue : function(){
48307         var dom = this.el.dom;
48308         this.value = dom.options[dom.selectedIndex].value;
48309         return this.value;
48310         
48311     },
48312
48313     /**
48314      * Clears any text/value currently set in the field
48315      */
48316     clearValue : function(){
48317         this.value = '';
48318         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48319         
48320     },
48321
48322     /**
48323      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48324      * will be displayed in the field.  If the value does not match the data value of an existing item,
48325      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48326      * Otherwise the field will be blank (although the value will still be set).
48327      * @param {String} value The value to match
48328      */
48329     setValue : function(v){
48330         var d = this.el.dom;
48331         for (var i =0; i < d.options.length;i++) {
48332             if (v == d.options[i].value) {
48333                 d.selectedIndex = i;
48334                 this.value = v;
48335                 return;
48336             }
48337         }
48338         this.clearValue();
48339     },
48340     /**
48341      * @property {Object} the last set data for the element
48342      */
48343     
48344     lastData : false,
48345     /**
48346      * Sets the value of the field based on a object which is related to the record format for the store.
48347      * @param {Object} value the value to set as. or false on reset?
48348      */
48349     setFromData : function(o){
48350         Roo.log('setfrom data?');
48351          
48352         
48353         
48354     },
48355     // private
48356     reset : function(){
48357         this.clearValue();
48358     },
48359     // private
48360     findRecord : function(prop, value){
48361         
48362         return false;
48363     
48364         var record;
48365         if(this.store.getCount() > 0){
48366             this.store.each(function(r){
48367                 if(r.data[prop] == value){
48368                     record = r;
48369                     return false;
48370                 }
48371                 return true;
48372             });
48373         }
48374         return record;
48375     },
48376     
48377     getName: function()
48378     {
48379         // returns hidden if it's set..
48380         if (!this.rendered) {return ''};
48381         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48382         
48383     },
48384      
48385
48386     
48387
48388     // private
48389     onEmptyResults : function(){
48390         Roo.log('empty results');
48391         //this.collapse();
48392     },
48393
48394     /**
48395      * Returns true if the dropdown list is expanded, else false.
48396      */
48397     isExpanded : function(){
48398         return false;
48399     },
48400
48401     /**
48402      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48403      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48404      * @param {String} value The data value of the item to select
48405      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48406      * selected item if it is not currently in view (defaults to true)
48407      * @return {Boolean} True if the value matched an item in the list, else false
48408      */
48409     selectByValue : function(v, scrollIntoView){
48410         Roo.log('select By Value');
48411         return false;
48412     
48413         if(v !== undefined && v !== null){
48414             var r = this.findRecord(this.valueField || this.displayField, v);
48415             if(r){
48416                 this.select(this.store.indexOf(r), scrollIntoView);
48417                 return true;
48418             }
48419         }
48420         return false;
48421     },
48422
48423     /**
48424      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48425      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48426      * @param {Number} index The zero-based index of the list item to select
48427      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48428      * selected item if it is not currently in view (defaults to true)
48429      */
48430     select : function(index, scrollIntoView){
48431         Roo.log('select ');
48432         return  ;
48433         
48434         this.selectedIndex = index;
48435         this.view.select(index);
48436         if(scrollIntoView !== false){
48437             var el = this.view.getNode(index);
48438             if(el){
48439                 this.innerList.scrollChildIntoView(el, false);
48440             }
48441         }
48442     },
48443
48444       
48445
48446     // private
48447     validateBlur : function(){
48448         
48449         return;
48450         
48451     },
48452
48453     // private
48454     initQuery : function(){
48455         this.doQuery(this.getRawValue());
48456     },
48457
48458     // private
48459     doForce : function(){
48460         if(this.el.dom.value.length > 0){
48461             this.el.dom.value =
48462                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48463              
48464         }
48465     },
48466
48467     /**
48468      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48469      * query allowing the query action to be canceled if needed.
48470      * @param {String} query The SQL query to execute
48471      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48472      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48473      * saved in the current store (defaults to false)
48474      */
48475     doQuery : function(q, forceAll){
48476         
48477         Roo.log('doQuery?');
48478         if(q === undefined || q === null){
48479             q = '';
48480         }
48481         var qe = {
48482             query: q,
48483             forceAll: forceAll,
48484             combo: this,
48485             cancel:false
48486         };
48487         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48488             return false;
48489         }
48490         q = qe.query;
48491         forceAll = qe.forceAll;
48492         if(forceAll === true || (q.length >= this.minChars)){
48493             if(this.lastQuery != q || this.alwaysQuery){
48494                 this.lastQuery = q;
48495                 if(this.mode == 'local'){
48496                     this.selectedIndex = -1;
48497                     if(forceAll){
48498                         this.store.clearFilter();
48499                     }else{
48500                         this.store.filter(this.displayField, q);
48501                     }
48502                     this.onLoad();
48503                 }else{
48504                     this.store.baseParams[this.queryParam] = q;
48505                     this.store.load({
48506                         params: this.getParams(q)
48507                     });
48508                     this.expand();
48509                 }
48510             }else{
48511                 this.selectedIndex = -1;
48512                 this.onLoad();   
48513             }
48514         }
48515     },
48516
48517     // private
48518     getParams : function(q){
48519         var p = {};
48520         //p[this.queryParam] = q;
48521         if(this.pageSize){
48522             p.start = 0;
48523             p.limit = this.pageSize;
48524         }
48525         return p;
48526     },
48527
48528     /**
48529      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48530      */
48531     collapse : function(){
48532         
48533     },
48534
48535     // private
48536     collapseIf : function(e){
48537         
48538     },
48539
48540     /**
48541      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48542      */
48543     expand : function(){
48544         
48545     } ,
48546
48547     // private
48548      
48549
48550     /** 
48551     * @cfg {Boolean} grow 
48552     * @hide 
48553     */
48554     /** 
48555     * @cfg {Number} growMin 
48556     * @hide 
48557     */
48558     /** 
48559     * @cfg {Number} growMax 
48560     * @hide 
48561     */
48562     /**
48563      * @hide
48564      * @method autoSize
48565      */
48566     
48567     setWidth : function()
48568     {
48569         
48570     },
48571     getResizeEl : function(){
48572         return this.el;
48573     }
48574 });//<script type="text/javasscript">
48575  
48576
48577 /**
48578  * @class Roo.DDView
48579  * A DnD enabled version of Roo.View.
48580  * @param {Element/String} container The Element in which to create the View.
48581  * @param {String} tpl The template string used to create the markup for each element of the View
48582  * @param {Object} config The configuration properties. These include all the config options of
48583  * {@link Roo.View} plus some specific to this class.<br>
48584  * <p>
48585  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48586  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48587  * <p>
48588  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48589 .x-view-drag-insert-above {
48590         border-top:1px dotted #3366cc;
48591 }
48592 .x-view-drag-insert-below {
48593         border-bottom:1px dotted #3366cc;
48594 }
48595 </code></pre>
48596  * 
48597  */
48598  
48599 Roo.DDView = function(container, tpl, config) {
48600     Roo.DDView.superclass.constructor.apply(this, arguments);
48601     this.getEl().setStyle("outline", "0px none");
48602     this.getEl().unselectable();
48603     if (this.dragGroup) {
48604                 this.setDraggable(this.dragGroup.split(","));
48605     }
48606     if (this.dropGroup) {
48607                 this.setDroppable(this.dropGroup.split(","));
48608     }
48609     if (this.deletable) {
48610         this.setDeletable();
48611     }
48612     this.isDirtyFlag = false;
48613         this.addEvents({
48614                 "drop" : true
48615         });
48616 };
48617
48618 Roo.extend(Roo.DDView, Roo.View, {
48619 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48620 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48621 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48622 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48623
48624         isFormField: true,
48625
48626         reset: Roo.emptyFn,
48627         
48628         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48629
48630         validate: function() {
48631                 return true;
48632         },
48633         
48634         destroy: function() {
48635                 this.purgeListeners();
48636                 this.getEl.removeAllListeners();
48637                 this.getEl().remove();
48638                 if (this.dragZone) {
48639                         if (this.dragZone.destroy) {
48640                                 this.dragZone.destroy();
48641                         }
48642                 }
48643                 if (this.dropZone) {
48644                         if (this.dropZone.destroy) {
48645                                 this.dropZone.destroy();
48646                         }
48647                 }
48648         },
48649
48650 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48651         getName: function() {
48652                 return this.name;
48653         },
48654
48655 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48656         setValue: function(v) {
48657                 if (!this.store) {
48658                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48659                 }
48660                 var data = {};
48661                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48662                 this.store.proxy = new Roo.data.MemoryProxy(data);
48663                 this.store.load();
48664         },
48665
48666 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48667         getValue: function() {
48668                 var result = '(';
48669                 this.store.each(function(rec) {
48670                         result += rec.id + ',';
48671                 });
48672                 return result.substr(0, result.length - 1) + ')';
48673         },
48674         
48675         getIds: function() {
48676                 var i = 0, result = new Array(this.store.getCount());
48677                 this.store.each(function(rec) {
48678                         result[i++] = rec.id;
48679                 });
48680                 return result;
48681         },
48682         
48683         isDirty: function() {
48684                 return this.isDirtyFlag;
48685         },
48686
48687 /**
48688  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48689  *      whole Element becomes the target, and this causes the drop gesture to append.
48690  */
48691     getTargetFromEvent : function(e) {
48692                 var target = e.getTarget();
48693                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48694                 target = target.parentNode;
48695                 }
48696                 if (!target) {
48697                         target = this.el.dom.lastChild || this.el.dom;
48698                 }
48699                 return target;
48700     },
48701
48702 /**
48703  *      Create the drag data which consists of an object which has the property "ddel" as
48704  *      the drag proxy element. 
48705  */
48706     getDragData : function(e) {
48707         var target = this.findItemFromChild(e.getTarget());
48708                 if(target) {
48709                         this.handleSelection(e);
48710                         var selNodes = this.getSelectedNodes();
48711             var dragData = {
48712                 source: this,
48713                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48714                 nodes: selNodes,
48715                 records: []
48716                         };
48717                         var selectedIndices = this.getSelectedIndexes();
48718                         for (var i = 0; i < selectedIndices.length; i++) {
48719                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48720                         }
48721                         if (selNodes.length == 1) {
48722                                 dragData.ddel = target.cloneNode(true); // the div element
48723                         } else {
48724                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48725                                 div.className = 'multi-proxy';
48726                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48727                                         div.appendChild(selNodes[i].cloneNode(true));
48728                                 }
48729                                 dragData.ddel = div;
48730                         }
48731             //console.log(dragData)
48732             //console.log(dragData.ddel.innerHTML)
48733                         return dragData;
48734                 }
48735         //console.log('nodragData')
48736                 return false;
48737     },
48738     
48739 /**     Specify to which ddGroup items in this DDView may be dragged. */
48740     setDraggable: function(ddGroup) {
48741         if (ddGroup instanceof Array) {
48742                 Roo.each(ddGroup, this.setDraggable, this);
48743                 return;
48744         }
48745         if (this.dragZone) {
48746                 this.dragZone.addToGroup(ddGroup);
48747         } else {
48748                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48749                                 containerScroll: true,
48750                                 ddGroup: ddGroup 
48751
48752                         });
48753 //                      Draggability implies selection. DragZone's mousedown selects the element.
48754                         if (!this.multiSelect) { this.singleSelect = true; }
48755
48756 //                      Wire the DragZone's handlers up to methods in *this*
48757                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48758                 }
48759     },
48760
48761 /**     Specify from which ddGroup this DDView accepts drops. */
48762     setDroppable: function(ddGroup) {
48763         if (ddGroup instanceof Array) {
48764                 Roo.each(ddGroup, this.setDroppable, this);
48765                 return;
48766         }
48767         if (this.dropZone) {
48768                 this.dropZone.addToGroup(ddGroup);
48769         } else {
48770                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48771                                 containerScroll: true,
48772                                 ddGroup: ddGroup
48773                         });
48774
48775 //                      Wire the DropZone's handlers up to methods in *this*
48776                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48777                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48778                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48779                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48780                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48781                 }
48782     },
48783
48784 /**     Decide whether to drop above or below a View node. */
48785     getDropPoint : function(e, n, dd){
48786         if (n == this.el.dom) { return "above"; }
48787                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48788                 var c = t + (b - t) / 2;
48789                 var y = Roo.lib.Event.getPageY(e);
48790                 if(y <= c) {
48791                         return "above";
48792                 }else{
48793                         return "below";
48794                 }
48795     },
48796
48797     onNodeEnter : function(n, dd, e, data){
48798                 return false;
48799     },
48800     
48801     onNodeOver : function(n, dd, e, data){
48802                 var pt = this.getDropPoint(e, n, dd);
48803                 // set the insert point style on the target node
48804                 var dragElClass = this.dropNotAllowed;
48805                 if (pt) {
48806                         var targetElClass;
48807                         if (pt == "above"){
48808                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48809                                 targetElClass = "x-view-drag-insert-above";
48810                         } else {
48811                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48812                                 targetElClass = "x-view-drag-insert-below";
48813                         }
48814                         if (this.lastInsertClass != targetElClass){
48815                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48816                                 this.lastInsertClass = targetElClass;
48817                         }
48818                 }
48819                 return dragElClass;
48820         },
48821
48822     onNodeOut : function(n, dd, e, data){
48823                 this.removeDropIndicators(n);
48824     },
48825
48826     onNodeDrop : function(n, dd, e, data){
48827         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48828                 return false;
48829         }
48830         var pt = this.getDropPoint(e, n, dd);
48831                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48832                 if (pt == "below") { insertAt++; }
48833                 for (var i = 0; i < data.records.length; i++) {
48834                         var r = data.records[i];
48835                         var dup = this.store.getById(r.id);
48836                         if (dup && (dd != this.dragZone)) {
48837                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48838                         } else {
48839                                 if (data.copy) {
48840                                         this.store.insert(insertAt++, r.copy());
48841                                 } else {
48842                                         data.source.isDirtyFlag = true;
48843                                         r.store.remove(r);
48844                                         this.store.insert(insertAt++, r);
48845                                 }
48846                                 this.isDirtyFlag = true;
48847                         }
48848                 }
48849                 this.dragZone.cachedTarget = null;
48850                 return true;
48851     },
48852
48853     removeDropIndicators : function(n){
48854                 if(n){
48855                         Roo.fly(n).removeClass([
48856                                 "x-view-drag-insert-above",
48857                                 "x-view-drag-insert-below"]);
48858                         this.lastInsertClass = "_noclass";
48859                 }
48860     },
48861
48862 /**
48863  *      Utility method. Add a delete option to the DDView's context menu.
48864  *      @param {String} imageUrl The URL of the "delete" icon image.
48865  */
48866         setDeletable: function(imageUrl) {
48867                 if (!this.singleSelect && !this.multiSelect) {
48868                         this.singleSelect = true;
48869                 }
48870                 var c = this.getContextMenu();
48871                 this.contextMenu.on("itemclick", function(item) {
48872                         switch (item.id) {
48873                                 case "delete":
48874                                         this.remove(this.getSelectedIndexes());
48875                                         break;
48876                         }
48877                 }, this);
48878                 this.contextMenu.add({
48879                         icon: imageUrl,
48880                         id: "delete",
48881                         text: 'Delete'
48882                 });
48883         },
48884         
48885 /**     Return the context menu for this DDView. */
48886         getContextMenu: function() {
48887                 if (!this.contextMenu) {
48888 //                      Create the View's context menu
48889                         this.contextMenu = new Roo.menu.Menu({
48890                                 id: this.id + "-contextmenu"
48891                         });
48892                         this.el.on("contextmenu", this.showContextMenu, this);
48893                 }
48894                 return this.contextMenu;
48895         },
48896         
48897         disableContextMenu: function() {
48898                 if (this.contextMenu) {
48899                         this.el.un("contextmenu", this.showContextMenu, this);
48900                 }
48901         },
48902
48903         showContextMenu: function(e, item) {
48904         item = this.findItemFromChild(e.getTarget());
48905                 if (item) {
48906                         e.stopEvent();
48907                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48908                         this.contextMenu.showAt(e.getXY());
48909             }
48910     },
48911
48912 /**
48913  *      Remove {@link Roo.data.Record}s at the specified indices.
48914  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48915  */
48916     remove: function(selectedIndices) {
48917                 selectedIndices = [].concat(selectedIndices);
48918                 for (var i = 0; i < selectedIndices.length; i++) {
48919                         var rec = this.store.getAt(selectedIndices[i]);
48920                         this.store.remove(rec);
48921                 }
48922     },
48923
48924 /**
48925  *      Double click fires the event, but also, if this is draggable, and there is only one other
48926  *      related DropZone, it transfers the selected node.
48927  */
48928     onDblClick : function(e){
48929         var item = this.findItemFromChild(e.getTarget());
48930         if(item){
48931             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48932                 return false;
48933             }
48934             if (this.dragGroup) {
48935                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48936                     while (targets.indexOf(this.dropZone) > -1) {
48937                             targets.remove(this.dropZone);
48938                                 }
48939                     if (targets.length == 1) {
48940                                         this.dragZone.cachedTarget = null;
48941                         var el = Roo.get(targets[0].getEl());
48942                         var box = el.getBox(true);
48943                         targets[0].onNodeDrop(el.dom, {
48944                                 target: el.dom,
48945                                 xy: [box.x, box.y + box.height - 1]
48946                         }, null, this.getDragData(e));
48947                     }
48948                 }
48949         }
48950     },
48951     
48952     handleSelection: function(e) {
48953                 this.dragZone.cachedTarget = null;
48954         var item = this.findItemFromChild(e.getTarget());
48955         if (!item) {
48956                 this.clearSelections(true);
48957                 return;
48958         }
48959                 if (item && (this.multiSelect || this.singleSelect)){
48960                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48961                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48962                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48963                                 this.unselect(item);
48964                         } else {
48965                                 this.select(item, this.multiSelect && e.ctrlKey);
48966                                 this.lastSelection = item;
48967                         }
48968                 }
48969     },
48970
48971     onItemClick : function(item, index, e){
48972                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48973                         return false;
48974                 }
48975                 return true;
48976     },
48977
48978     unselect : function(nodeInfo, suppressEvent){
48979                 var node = this.getNode(nodeInfo);
48980                 if(node && this.isSelected(node)){
48981                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48982                                 Roo.fly(node).removeClass(this.selectedClass);
48983                                 this.selections.remove(node);
48984                                 if(!suppressEvent){
48985                                         this.fireEvent("selectionchange", this, this.selections);
48986                                 }
48987                         }
48988                 }
48989     }
48990 });
48991 /*
48992  * Based on:
48993  * Ext JS Library 1.1.1
48994  * Copyright(c) 2006-2007, Ext JS, LLC.
48995  *
48996  * Originally Released Under LGPL - original licence link has changed is not relivant.
48997  *
48998  * Fork - LGPL
48999  * <script type="text/javascript">
49000  */
49001  
49002 /**
49003  * @class Roo.LayoutManager
49004  * @extends Roo.util.Observable
49005  * Base class for layout managers.
49006  */
49007 Roo.LayoutManager = function(container, config){
49008     Roo.LayoutManager.superclass.constructor.call(this);
49009     this.el = Roo.get(container);
49010     // ie scrollbar fix
49011     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49012         document.body.scroll = "no";
49013     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49014         this.el.position('relative');
49015     }
49016     this.id = this.el.id;
49017     this.el.addClass("x-layout-container");
49018     /** false to disable window resize monitoring @type Boolean */
49019     this.monitorWindowResize = true;
49020     this.regions = {};
49021     this.addEvents({
49022         /**
49023          * @event layout
49024          * Fires when a layout is performed. 
49025          * @param {Roo.LayoutManager} this
49026          */
49027         "layout" : true,
49028         /**
49029          * @event regionresized
49030          * Fires when the user resizes a region. 
49031          * @param {Roo.LayoutRegion} region The resized region
49032          * @param {Number} newSize The new size (width for east/west, height for north/south)
49033          */
49034         "regionresized" : true,
49035         /**
49036          * @event regioncollapsed
49037          * Fires when a region is collapsed. 
49038          * @param {Roo.LayoutRegion} region The collapsed region
49039          */
49040         "regioncollapsed" : true,
49041         /**
49042          * @event regionexpanded
49043          * Fires when a region is expanded.  
49044          * @param {Roo.LayoutRegion} region The expanded region
49045          */
49046         "regionexpanded" : true
49047     });
49048     this.updating = false;
49049     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49050 };
49051
49052 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49053     /**
49054      * Returns true if this layout is currently being updated
49055      * @return {Boolean}
49056      */
49057     isUpdating : function(){
49058         return this.updating; 
49059     },
49060     
49061     /**
49062      * Suspend the LayoutManager from doing auto-layouts while
49063      * making multiple add or remove calls
49064      */
49065     beginUpdate : function(){
49066         this.updating = true;    
49067     },
49068     
49069     /**
49070      * Restore auto-layouts and optionally disable the manager from performing a layout
49071      * @param {Boolean} noLayout true to disable a layout update 
49072      */
49073     endUpdate : function(noLayout){
49074         this.updating = false;
49075         if(!noLayout){
49076             this.layout();
49077         }    
49078     },
49079     
49080     layout: function(){
49081         
49082     },
49083     
49084     onRegionResized : function(region, newSize){
49085         this.fireEvent("regionresized", region, newSize);
49086         this.layout();
49087     },
49088     
49089     onRegionCollapsed : function(region){
49090         this.fireEvent("regioncollapsed", region);
49091     },
49092     
49093     onRegionExpanded : function(region){
49094         this.fireEvent("regionexpanded", region);
49095     },
49096         
49097     /**
49098      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49099      * performs box-model adjustments.
49100      * @return {Object} The size as an object {width: (the width), height: (the height)}
49101      */
49102     getViewSize : function(){
49103         var size;
49104         if(this.el.dom != document.body){
49105             size = this.el.getSize();
49106         }else{
49107             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49108         }
49109         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49110         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49111         return size;
49112     },
49113     
49114     /**
49115      * Returns the Element this layout is bound to.
49116      * @return {Roo.Element}
49117      */
49118     getEl : function(){
49119         return this.el;
49120     },
49121     
49122     /**
49123      * Returns the specified region.
49124      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49125      * @return {Roo.LayoutRegion}
49126      */
49127     getRegion : function(target){
49128         return this.regions[target.toLowerCase()];
49129     },
49130     
49131     onWindowResize : function(){
49132         if(this.monitorWindowResize){
49133             this.layout();
49134         }
49135     }
49136 });/*
49137  * Based on:
49138  * Ext JS Library 1.1.1
49139  * Copyright(c) 2006-2007, Ext JS, LLC.
49140  *
49141  * Originally Released Under LGPL - original licence link has changed is not relivant.
49142  *
49143  * Fork - LGPL
49144  * <script type="text/javascript">
49145  */
49146 /**
49147  * @class Roo.BorderLayout
49148  * @extends Roo.LayoutManager
49149  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49150  * please see: <br><br>
49151  * <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>
49152  * <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>
49153  * Example:
49154  <pre><code>
49155  var layout = new Roo.BorderLayout(document.body, {
49156     north: {
49157         initialSize: 25,
49158         titlebar: false
49159     },
49160     west: {
49161         split:true,
49162         initialSize: 200,
49163         minSize: 175,
49164         maxSize: 400,
49165         titlebar: true,
49166         collapsible: true
49167     },
49168     east: {
49169         split:true,
49170         initialSize: 202,
49171         minSize: 175,
49172         maxSize: 400,
49173         titlebar: true,
49174         collapsible: true
49175     },
49176     south: {
49177         split:true,
49178         initialSize: 100,
49179         minSize: 100,
49180         maxSize: 200,
49181         titlebar: true,
49182         collapsible: true
49183     },
49184     center: {
49185         titlebar: true,
49186         autoScroll:true,
49187         resizeTabs: true,
49188         minTabWidth: 50,
49189         preferredTabWidth: 150
49190     }
49191 });
49192
49193 // shorthand
49194 var CP = Roo.ContentPanel;
49195
49196 layout.beginUpdate();
49197 layout.add("north", new CP("north", "North"));
49198 layout.add("south", new CP("south", {title: "South", closable: true}));
49199 layout.add("west", new CP("west", {title: "West"}));
49200 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49201 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49202 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49203 layout.getRegion("center").showPanel("center1");
49204 layout.endUpdate();
49205 </code></pre>
49206
49207 <b>The container the layout is rendered into can be either the body element or any other element.
49208 If it is not the body element, the container needs to either be an absolute positioned element,
49209 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49210 the container size if it is not the body element.</b>
49211
49212 * @constructor
49213 * Create a new BorderLayout
49214 * @param {String/HTMLElement/Element} container The container this layout is bound to
49215 * @param {Object} config Configuration options
49216  */
49217 Roo.BorderLayout = function(container, config){
49218     config = config || {};
49219     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49220     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49221     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49222         var target = this.factory.validRegions[i];
49223         if(config[target]){
49224             this.addRegion(target, config[target]);
49225         }
49226     }
49227 };
49228
49229 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49230     /**
49231      * Creates and adds a new region if it doesn't already exist.
49232      * @param {String} target The target region key (north, south, east, west or center).
49233      * @param {Object} config The regions config object
49234      * @return {BorderLayoutRegion} The new region
49235      */
49236     addRegion : function(target, config){
49237         if(!this.regions[target]){
49238             var r = this.factory.create(target, this, config);
49239             this.bindRegion(target, r);
49240         }
49241         return this.regions[target];
49242     },
49243
49244     // private (kinda)
49245     bindRegion : function(name, r){
49246         this.regions[name] = r;
49247         r.on("visibilitychange", this.layout, this);
49248         r.on("paneladded", this.layout, this);
49249         r.on("panelremoved", this.layout, this);
49250         r.on("invalidated", this.layout, this);
49251         r.on("resized", this.onRegionResized, this);
49252         r.on("collapsed", this.onRegionCollapsed, this);
49253         r.on("expanded", this.onRegionExpanded, this);
49254     },
49255
49256     /**
49257      * Performs a layout update.
49258      */
49259     layout : function(){
49260         if(this.updating) return;
49261         var size = this.getViewSize();
49262         var w = size.width;
49263         var h = size.height;
49264         var centerW = w;
49265         var centerH = h;
49266         var centerY = 0;
49267         var centerX = 0;
49268         //var x = 0, y = 0;
49269
49270         var rs = this.regions;
49271         var north = rs["north"];
49272         var south = rs["south"]; 
49273         var west = rs["west"];
49274         var east = rs["east"];
49275         var center = rs["center"];
49276         //if(this.hideOnLayout){ // not supported anymore
49277             //c.el.setStyle("display", "none");
49278         //}
49279         if(north && north.isVisible()){
49280             var b = north.getBox();
49281             var m = north.getMargins();
49282             b.width = w - (m.left+m.right);
49283             b.x = m.left;
49284             b.y = m.top;
49285             centerY = b.height + b.y + m.bottom;
49286             centerH -= centerY;
49287             north.updateBox(this.safeBox(b));
49288         }
49289         if(south && south.isVisible()){
49290             var b = south.getBox();
49291             var m = south.getMargins();
49292             b.width = w - (m.left+m.right);
49293             b.x = m.left;
49294             var totalHeight = (b.height + m.top + m.bottom);
49295             b.y = h - totalHeight + m.top;
49296             centerH -= totalHeight;
49297             south.updateBox(this.safeBox(b));
49298         }
49299         if(west && west.isVisible()){
49300             var b = west.getBox();
49301             var m = west.getMargins();
49302             b.height = centerH - (m.top+m.bottom);
49303             b.x = m.left;
49304             b.y = centerY + m.top;
49305             var totalWidth = (b.width + m.left + m.right);
49306             centerX += totalWidth;
49307             centerW -= totalWidth;
49308             west.updateBox(this.safeBox(b));
49309         }
49310         if(east && east.isVisible()){
49311             var b = east.getBox();
49312             var m = east.getMargins();
49313             b.height = centerH - (m.top+m.bottom);
49314             var totalWidth = (b.width + m.left + m.right);
49315             b.x = w - totalWidth + m.left;
49316             b.y = centerY + m.top;
49317             centerW -= totalWidth;
49318             east.updateBox(this.safeBox(b));
49319         }
49320         if(center){
49321             var m = center.getMargins();
49322             var centerBox = {
49323                 x: centerX + m.left,
49324                 y: centerY + m.top,
49325                 width: centerW - (m.left+m.right),
49326                 height: centerH - (m.top+m.bottom)
49327             };
49328             //if(this.hideOnLayout){
49329                 //center.el.setStyle("display", "block");
49330             //}
49331             center.updateBox(this.safeBox(centerBox));
49332         }
49333         this.el.repaint();
49334         this.fireEvent("layout", this);
49335     },
49336
49337     // private
49338     safeBox : function(box){
49339         box.width = Math.max(0, box.width);
49340         box.height = Math.max(0, box.height);
49341         return box;
49342     },
49343
49344     /**
49345      * Adds a ContentPanel (or subclass) to this layout.
49346      * @param {String} target The target region key (north, south, east, west or center).
49347      * @param {Roo.ContentPanel} panel The panel to add
49348      * @return {Roo.ContentPanel} The added panel
49349      */
49350     add : function(target, panel){
49351          
49352         target = target.toLowerCase();
49353         return this.regions[target].add(panel);
49354     },
49355
49356     /**
49357      * Remove a ContentPanel (or subclass) to this layout.
49358      * @param {String} target The target region key (north, south, east, west or center).
49359      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49360      * @return {Roo.ContentPanel} The removed panel
49361      */
49362     remove : function(target, panel){
49363         target = target.toLowerCase();
49364         return this.regions[target].remove(panel);
49365     },
49366
49367     /**
49368      * Searches all regions for a panel with the specified id
49369      * @param {String} panelId
49370      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49371      */
49372     findPanel : function(panelId){
49373         var rs = this.regions;
49374         for(var target in rs){
49375             if(typeof rs[target] != "function"){
49376                 var p = rs[target].getPanel(panelId);
49377                 if(p){
49378                     return p;
49379                 }
49380             }
49381         }
49382         return null;
49383     },
49384
49385     /**
49386      * Searches all regions for a panel with the specified id and activates (shows) it.
49387      * @param {String/ContentPanel} panelId The panels id or the panel itself
49388      * @return {Roo.ContentPanel} The shown panel or null
49389      */
49390     showPanel : function(panelId) {
49391       var rs = this.regions;
49392       for(var target in rs){
49393          var r = rs[target];
49394          if(typeof r != "function"){
49395             if(r.hasPanel(panelId)){
49396                return r.showPanel(panelId);
49397             }
49398          }
49399       }
49400       return null;
49401    },
49402
49403    /**
49404      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49405      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49406      */
49407     restoreState : function(provider){
49408         if(!provider){
49409             provider = Roo.state.Manager;
49410         }
49411         var sm = new Roo.LayoutStateManager();
49412         sm.init(this, provider);
49413     },
49414
49415     /**
49416      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49417      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49418      * a valid ContentPanel config object.  Example:
49419      * <pre><code>
49420 // Create the main layout
49421 var layout = new Roo.BorderLayout('main-ct', {
49422     west: {
49423         split:true,
49424         minSize: 175,
49425         titlebar: true
49426     },
49427     center: {
49428         title:'Components'
49429     }
49430 }, 'main-ct');
49431
49432 // Create and add multiple ContentPanels at once via configs
49433 layout.batchAdd({
49434    west: {
49435        id: 'source-files',
49436        autoCreate:true,
49437        title:'Ext Source Files',
49438        autoScroll:true,
49439        fitToFrame:true
49440    },
49441    center : {
49442        el: cview,
49443        autoScroll:true,
49444        fitToFrame:true,
49445        toolbar: tb,
49446        resizeEl:'cbody'
49447    }
49448 });
49449 </code></pre>
49450      * @param {Object} regions An object containing ContentPanel configs by region name
49451      */
49452     batchAdd : function(regions){
49453         this.beginUpdate();
49454         for(var rname in regions){
49455             var lr = this.regions[rname];
49456             if(lr){
49457                 this.addTypedPanels(lr, regions[rname]);
49458             }
49459         }
49460         this.endUpdate();
49461     },
49462
49463     // private
49464     addTypedPanels : function(lr, ps){
49465         if(typeof ps == 'string'){
49466             lr.add(new Roo.ContentPanel(ps));
49467         }
49468         else if(ps instanceof Array){
49469             for(var i =0, len = ps.length; i < len; i++){
49470                 this.addTypedPanels(lr, ps[i]);
49471             }
49472         }
49473         else if(!ps.events){ // raw config?
49474             var el = ps.el;
49475             delete ps.el; // prevent conflict
49476             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49477         }
49478         else {  // panel object assumed!
49479             lr.add(ps);
49480         }
49481     },
49482     /**
49483      * Adds a xtype elements to the layout.
49484      * <pre><code>
49485
49486 layout.addxtype({
49487        xtype : 'ContentPanel',
49488        region: 'west',
49489        items: [ .... ]
49490    }
49491 );
49492
49493 layout.addxtype({
49494         xtype : 'NestedLayoutPanel',
49495         region: 'west',
49496         layout: {
49497            center: { },
49498            west: { }   
49499         },
49500         items : [ ... list of content panels or nested layout panels.. ]
49501    }
49502 );
49503 </code></pre>
49504      * @param {Object} cfg Xtype definition of item to add.
49505      */
49506     addxtype : function(cfg)
49507     {
49508         // basically accepts a pannel...
49509         // can accept a layout region..!?!?
49510         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49511         
49512         if (!cfg.xtype.match(/Panel$/)) {
49513             return false;
49514         }
49515         var ret = false;
49516         
49517         if (typeof(cfg.region) == 'undefined') {
49518             Roo.log("Failed to add Panel, region was not set");
49519             Roo.log(cfg);
49520             return false;
49521         }
49522         var region = cfg.region;
49523         delete cfg.region;
49524         
49525           
49526         var xitems = [];
49527         if (cfg.items) {
49528             xitems = cfg.items;
49529             delete cfg.items;
49530         }
49531         var nb = false;
49532         
49533         switch(cfg.xtype) 
49534         {
49535             case 'ContentPanel':  // ContentPanel (el, cfg)
49536             case 'ScrollPanel':  // ContentPanel (el, cfg)
49537             case 'ViewPanel': 
49538                 if(cfg.autoCreate) {
49539                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49540                 } else {
49541                     var el = this.el.createChild();
49542                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49543                 }
49544                 
49545                 this.add(region, ret);
49546                 break;
49547             
49548             
49549             case 'TreePanel': // our new panel!
49550                 cfg.el = this.el.createChild();
49551                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49552                 this.add(region, ret);
49553                 break;
49554             
49555             case 'NestedLayoutPanel': 
49556                 // create a new Layout (which is  a Border Layout...
49557                 var el = this.el.createChild();
49558                 var clayout = cfg.layout;
49559                 delete cfg.layout;
49560                 clayout.items   = clayout.items  || [];
49561                 // replace this exitems with the clayout ones..
49562                 xitems = clayout.items;
49563                  
49564                 
49565                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49566                     cfg.background = false;
49567                 }
49568                 var layout = new Roo.BorderLayout(el, clayout);
49569                 
49570                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49571                 //console.log('adding nested layout panel '  + cfg.toSource());
49572                 this.add(region, ret);
49573                 nb = {}; /// find first...
49574                 break;
49575                 
49576             case 'GridPanel': 
49577             
49578                 // needs grid and region
49579                 
49580                 //var el = this.getRegion(region).el.createChild();
49581                 var el = this.el.createChild();
49582                 // create the grid first...
49583                 
49584                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49585                 delete cfg.grid;
49586                 if (region == 'center' && this.active ) {
49587                     cfg.background = false;
49588                 }
49589                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49590                 
49591                 this.add(region, ret);
49592                 if (cfg.background) {
49593                     ret.on('activate', function(gp) {
49594                         if (!gp.grid.rendered) {
49595                             gp.grid.render();
49596                         }
49597                     });
49598                 } else {
49599                     grid.render();
49600                 }
49601                 break;
49602            
49603            
49604            
49605                 
49606                 
49607                 
49608             default:
49609                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49610                     
49611                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49612                     this.add(region, ret);
49613                 } else {
49614                 
49615                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49616                     return null;
49617                 }
49618                 
49619              // GridPanel (grid, cfg)
49620             
49621         }
49622         this.beginUpdate();
49623         // add children..
49624         var region = '';
49625         var abn = {};
49626         Roo.each(xitems, function(i)  {
49627             region = nb && i.region ? i.region : false;
49628             
49629             var add = ret.addxtype(i);
49630            
49631             if (region) {
49632                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49633                 if (!i.background) {
49634                     abn[region] = nb[region] ;
49635                 }
49636             }
49637             
49638         });
49639         this.endUpdate();
49640
49641         // make the last non-background panel active..
49642         //if (nb) { Roo.log(abn); }
49643         if (nb) {
49644             
49645             for(var r in abn) {
49646                 region = this.getRegion(r);
49647                 if (region) {
49648                     // tried using nb[r], but it does not work..
49649                      
49650                     region.showPanel(abn[r]);
49651                    
49652                 }
49653             }
49654         }
49655         return ret;
49656         
49657     }
49658 });
49659
49660 /**
49661  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49662  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49663  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49664  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49665  * <pre><code>
49666 // shorthand
49667 var CP = Roo.ContentPanel;
49668
49669 var layout = Roo.BorderLayout.create({
49670     north: {
49671         initialSize: 25,
49672         titlebar: false,
49673         panels: [new CP("north", "North")]
49674     },
49675     west: {
49676         split:true,
49677         initialSize: 200,
49678         minSize: 175,
49679         maxSize: 400,
49680         titlebar: true,
49681         collapsible: true,
49682         panels: [new CP("west", {title: "West"})]
49683     },
49684     east: {
49685         split:true,
49686         initialSize: 202,
49687         minSize: 175,
49688         maxSize: 400,
49689         titlebar: true,
49690         collapsible: true,
49691         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49692     },
49693     south: {
49694         split:true,
49695         initialSize: 100,
49696         minSize: 100,
49697         maxSize: 200,
49698         titlebar: true,
49699         collapsible: true,
49700         panels: [new CP("south", {title: "South", closable: true})]
49701     },
49702     center: {
49703         titlebar: true,
49704         autoScroll:true,
49705         resizeTabs: true,
49706         minTabWidth: 50,
49707         preferredTabWidth: 150,
49708         panels: [
49709             new CP("center1", {title: "Close Me", closable: true}),
49710             new CP("center2", {title: "Center Panel", closable: false})
49711         ]
49712     }
49713 }, document.body);
49714
49715 layout.getRegion("center").showPanel("center1");
49716 </code></pre>
49717  * @param config
49718  * @param targetEl
49719  */
49720 Roo.BorderLayout.create = function(config, targetEl){
49721     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49722     layout.beginUpdate();
49723     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49724     for(var j = 0, jlen = regions.length; j < jlen; j++){
49725         var lr = regions[j];
49726         if(layout.regions[lr] && config[lr].panels){
49727             var r = layout.regions[lr];
49728             var ps = config[lr].panels;
49729             layout.addTypedPanels(r, ps);
49730         }
49731     }
49732     layout.endUpdate();
49733     return layout;
49734 };
49735
49736 // private
49737 Roo.BorderLayout.RegionFactory = {
49738     // private
49739     validRegions : ["north","south","east","west","center"],
49740
49741     // private
49742     create : function(target, mgr, config){
49743         target = target.toLowerCase();
49744         if(config.lightweight || config.basic){
49745             return new Roo.BasicLayoutRegion(mgr, config, target);
49746         }
49747         switch(target){
49748             case "north":
49749                 return new Roo.NorthLayoutRegion(mgr, config);
49750             case "south":
49751                 return new Roo.SouthLayoutRegion(mgr, config);
49752             case "east":
49753                 return new Roo.EastLayoutRegion(mgr, config);
49754             case "west":
49755                 return new Roo.WestLayoutRegion(mgr, config);
49756             case "center":
49757                 return new Roo.CenterLayoutRegion(mgr, config);
49758         }
49759         throw 'Layout region "'+target+'" not supported.';
49760     }
49761 };/*
49762  * Based on:
49763  * Ext JS Library 1.1.1
49764  * Copyright(c) 2006-2007, Ext JS, LLC.
49765  *
49766  * Originally Released Under LGPL - original licence link has changed is not relivant.
49767  *
49768  * Fork - LGPL
49769  * <script type="text/javascript">
49770  */
49771  
49772 /**
49773  * @class Roo.BasicLayoutRegion
49774  * @extends Roo.util.Observable
49775  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49776  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49777  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49778  */
49779 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49780     this.mgr = mgr;
49781     this.position  = pos;
49782     this.events = {
49783         /**
49784          * @scope Roo.BasicLayoutRegion
49785          */
49786         
49787         /**
49788          * @event beforeremove
49789          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49790          * @param {Roo.LayoutRegion} this
49791          * @param {Roo.ContentPanel} panel The panel
49792          * @param {Object} e The cancel event object
49793          */
49794         "beforeremove" : true,
49795         /**
49796          * @event invalidated
49797          * Fires when the layout for this region is changed.
49798          * @param {Roo.LayoutRegion} this
49799          */
49800         "invalidated" : true,
49801         /**
49802          * @event visibilitychange
49803          * Fires when this region is shown or hidden 
49804          * @param {Roo.LayoutRegion} this
49805          * @param {Boolean} visibility true or false
49806          */
49807         "visibilitychange" : true,
49808         /**
49809          * @event paneladded
49810          * Fires when a panel is added. 
49811          * @param {Roo.LayoutRegion} this
49812          * @param {Roo.ContentPanel} panel The panel
49813          */
49814         "paneladded" : true,
49815         /**
49816          * @event panelremoved
49817          * Fires when a panel is removed. 
49818          * @param {Roo.LayoutRegion} this
49819          * @param {Roo.ContentPanel} panel The panel
49820          */
49821         "panelremoved" : true,
49822         /**
49823          * @event collapsed
49824          * Fires when this region is collapsed.
49825          * @param {Roo.LayoutRegion} this
49826          */
49827         "collapsed" : true,
49828         /**
49829          * @event expanded
49830          * Fires when this region is expanded.
49831          * @param {Roo.LayoutRegion} this
49832          */
49833         "expanded" : true,
49834         /**
49835          * @event slideshow
49836          * Fires when this region is slid into view.
49837          * @param {Roo.LayoutRegion} this
49838          */
49839         "slideshow" : true,
49840         /**
49841          * @event slidehide
49842          * Fires when this region slides out of view. 
49843          * @param {Roo.LayoutRegion} this
49844          */
49845         "slidehide" : true,
49846         /**
49847          * @event panelactivated
49848          * Fires when a panel is activated. 
49849          * @param {Roo.LayoutRegion} this
49850          * @param {Roo.ContentPanel} panel The activated panel
49851          */
49852         "panelactivated" : true,
49853         /**
49854          * @event resized
49855          * Fires when the user resizes this region. 
49856          * @param {Roo.LayoutRegion} this
49857          * @param {Number} newSize The new size (width for east/west, height for north/south)
49858          */
49859         "resized" : true
49860     };
49861     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49862     this.panels = new Roo.util.MixedCollection();
49863     this.panels.getKey = this.getPanelId.createDelegate(this);
49864     this.box = null;
49865     this.activePanel = null;
49866     // ensure listeners are added...
49867     
49868     if (config.listeners || config.events) {
49869         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49870             listeners : config.listeners || {},
49871             events : config.events || {}
49872         });
49873     }
49874     
49875     if(skipConfig !== true){
49876         this.applyConfig(config);
49877     }
49878 };
49879
49880 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49881     getPanelId : function(p){
49882         return p.getId();
49883     },
49884     
49885     applyConfig : function(config){
49886         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49887         this.config = config;
49888         
49889     },
49890     
49891     /**
49892      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49893      * the width, for horizontal (north, south) the height.
49894      * @param {Number} newSize The new width or height
49895      */
49896     resizeTo : function(newSize){
49897         var el = this.el ? this.el :
49898                  (this.activePanel ? this.activePanel.getEl() : null);
49899         if(el){
49900             switch(this.position){
49901                 case "east":
49902                 case "west":
49903                     el.setWidth(newSize);
49904                     this.fireEvent("resized", this, newSize);
49905                 break;
49906                 case "north":
49907                 case "south":
49908                     el.setHeight(newSize);
49909                     this.fireEvent("resized", this, newSize);
49910                 break;                
49911             }
49912         }
49913     },
49914     
49915     getBox : function(){
49916         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49917     },
49918     
49919     getMargins : function(){
49920         return this.margins;
49921     },
49922     
49923     updateBox : function(box){
49924         this.box = box;
49925         var el = this.activePanel.getEl();
49926         el.dom.style.left = box.x + "px";
49927         el.dom.style.top = box.y + "px";
49928         this.activePanel.setSize(box.width, box.height);
49929     },
49930     
49931     /**
49932      * Returns the container element for this region.
49933      * @return {Roo.Element}
49934      */
49935     getEl : function(){
49936         return this.activePanel;
49937     },
49938     
49939     /**
49940      * Returns true if this region is currently visible.
49941      * @return {Boolean}
49942      */
49943     isVisible : function(){
49944         return this.activePanel ? true : false;
49945     },
49946     
49947     setActivePanel : function(panel){
49948         panel = this.getPanel(panel);
49949         if(this.activePanel && this.activePanel != panel){
49950             this.activePanel.setActiveState(false);
49951             this.activePanel.getEl().setLeftTop(-10000,-10000);
49952         }
49953         this.activePanel = panel;
49954         panel.setActiveState(true);
49955         if(this.box){
49956             panel.setSize(this.box.width, this.box.height);
49957         }
49958         this.fireEvent("panelactivated", this, panel);
49959         this.fireEvent("invalidated");
49960     },
49961     
49962     /**
49963      * Show the specified panel.
49964      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49965      * @return {Roo.ContentPanel} The shown panel or null
49966      */
49967     showPanel : function(panel){
49968         if(panel = this.getPanel(panel)){
49969             this.setActivePanel(panel);
49970         }
49971         return panel;
49972     },
49973     
49974     /**
49975      * Get the active panel for this region.
49976      * @return {Roo.ContentPanel} The active panel or null
49977      */
49978     getActivePanel : function(){
49979         return this.activePanel;
49980     },
49981     
49982     /**
49983      * Add the passed ContentPanel(s)
49984      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49985      * @return {Roo.ContentPanel} The panel added (if only one was added)
49986      */
49987     add : function(panel){
49988         if(arguments.length > 1){
49989             for(var i = 0, len = arguments.length; i < len; i++) {
49990                 this.add(arguments[i]);
49991             }
49992             return null;
49993         }
49994         if(this.hasPanel(panel)){
49995             this.showPanel(panel);
49996             return panel;
49997         }
49998         var el = panel.getEl();
49999         if(el.dom.parentNode != this.mgr.el.dom){
50000             this.mgr.el.dom.appendChild(el.dom);
50001         }
50002         if(panel.setRegion){
50003             panel.setRegion(this);
50004         }
50005         this.panels.add(panel);
50006         el.setStyle("position", "absolute");
50007         if(!panel.background){
50008             this.setActivePanel(panel);
50009             if(this.config.initialSize && this.panels.getCount()==1){
50010                 this.resizeTo(this.config.initialSize);
50011             }
50012         }
50013         this.fireEvent("paneladded", this, panel);
50014         return panel;
50015     },
50016     
50017     /**
50018      * Returns true if the panel is in this region.
50019      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50020      * @return {Boolean}
50021      */
50022     hasPanel : function(panel){
50023         if(typeof panel == "object"){ // must be panel obj
50024             panel = panel.getId();
50025         }
50026         return this.getPanel(panel) ? true : false;
50027     },
50028     
50029     /**
50030      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50031      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50032      * @param {Boolean} preservePanel Overrides the config preservePanel option
50033      * @return {Roo.ContentPanel} The panel that was removed
50034      */
50035     remove : function(panel, preservePanel){
50036         panel = this.getPanel(panel);
50037         if(!panel){
50038             return null;
50039         }
50040         var e = {};
50041         this.fireEvent("beforeremove", this, panel, e);
50042         if(e.cancel === true){
50043             return null;
50044         }
50045         var panelId = panel.getId();
50046         this.panels.removeKey(panelId);
50047         return panel;
50048     },
50049     
50050     /**
50051      * Returns the panel specified or null if it's not in this region.
50052      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50053      * @return {Roo.ContentPanel}
50054      */
50055     getPanel : function(id){
50056         if(typeof id == "object"){ // must be panel obj
50057             return id;
50058         }
50059         return this.panels.get(id);
50060     },
50061     
50062     /**
50063      * Returns this regions position (north/south/east/west/center).
50064      * @return {String} 
50065      */
50066     getPosition: function(){
50067         return this.position;    
50068     }
50069 });/*
50070  * Based on:
50071  * Ext JS Library 1.1.1
50072  * Copyright(c) 2006-2007, Ext JS, LLC.
50073  *
50074  * Originally Released Under LGPL - original licence link has changed is not relivant.
50075  *
50076  * Fork - LGPL
50077  * <script type="text/javascript">
50078  */
50079  
50080 /**
50081  * @class Roo.LayoutRegion
50082  * @extends Roo.BasicLayoutRegion
50083  * This class represents a region in a layout manager.
50084  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50085  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50086  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50087  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50088  * @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})
50089  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50090  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50091  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50092  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50093  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50094  * @cfg {String}    title           The title for the region (overrides panel titles)
50095  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50096  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50097  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50098  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50099  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50100  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50101  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50102  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50103  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50104  * @cfg {Boolean}   showPin         True to show a pin button
50105  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50106  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50107  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50108  * @cfg {Number}    width           For East/West panels
50109  * @cfg {Number}    height          For North/South panels
50110  * @cfg {Boolean}   split           To show the splitter
50111  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50112  */
50113 Roo.LayoutRegion = function(mgr, config, pos){
50114     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50115     var dh = Roo.DomHelper;
50116     /** This region's container element 
50117     * @type Roo.Element */
50118     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50119     /** This region's title element 
50120     * @type Roo.Element */
50121
50122     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50123         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50124         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50125     ]}, true);
50126     this.titleEl.enableDisplayMode();
50127     /** This region's title text element 
50128     * @type HTMLElement */
50129     this.titleTextEl = this.titleEl.dom.firstChild;
50130     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50131     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50132     this.closeBtn.enableDisplayMode();
50133     this.closeBtn.on("click", this.closeClicked, this);
50134     this.closeBtn.hide();
50135
50136     this.createBody(config);
50137     this.visible = true;
50138     this.collapsed = false;
50139
50140     if(config.hideWhenEmpty){
50141         this.hide();
50142         this.on("paneladded", this.validateVisibility, this);
50143         this.on("panelremoved", this.validateVisibility, this);
50144     }
50145     this.applyConfig(config);
50146 };
50147
50148 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50149
50150     createBody : function(){
50151         /** This region's body element 
50152         * @type Roo.Element */
50153         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50154     },
50155
50156     applyConfig : function(c){
50157         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50158             var dh = Roo.DomHelper;
50159             if(c.titlebar !== false){
50160                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50161                 this.collapseBtn.on("click", this.collapse, this);
50162                 this.collapseBtn.enableDisplayMode();
50163
50164                 if(c.showPin === true || this.showPin){
50165                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50166                     this.stickBtn.enableDisplayMode();
50167                     this.stickBtn.on("click", this.expand, this);
50168                     this.stickBtn.hide();
50169                 }
50170             }
50171             /** This region's collapsed element
50172             * @type Roo.Element */
50173             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50174                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50175             ]}, true);
50176             if(c.floatable !== false){
50177                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50178                this.collapsedEl.on("click", this.collapseClick, this);
50179             }
50180
50181             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50182                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50183                    id: "message", unselectable: "on", style:{"float":"left"}});
50184                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50185              }
50186             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50187             this.expandBtn.on("click", this.expand, this);
50188         }
50189         if(this.collapseBtn){
50190             this.collapseBtn.setVisible(c.collapsible == true);
50191         }
50192         this.cmargins = c.cmargins || this.cmargins ||
50193                          (this.position == "west" || this.position == "east" ?
50194                              {top: 0, left: 2, right:2, bottom: 0} :
50195                              {top: 2, left: 0, right:0, bottom: 2});
50196         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50197         this.bottomTabs = c.tabPosition != "top";
50198         this.autoScroll = c.autoScroll || false;
50199         if(this.autoScroll){
50200             this.bodyEl.setStyle("overflow", "auto");
50201         }else{
50202             this.bodyEl.setStyle("overflow", "hidden");
50203         }
50204         //if(c.titlebar !== false){
50205             if((!c.titlebar && !c.title) || c.titlebar === false){
50206                 this.titleEl.hide();
50207             }else{
50208                 this.titleEl.show();
50209                 if(c.title){
50210                     this.titleTextEl.innerHTML = c.title;
50211                 }
50212             }
50213         //}
50214         this.duration = c.duration || .30;
50215         this.slideDuration = c.slideDuration || .45;
50216         this.config = c;
50217         if(c.collapsed){
50218             this.collapse(true);
50219         }
50220         if(c.hidden){
50221             this.hide();
50222         }
50223     },
50224     /**
50225      * Returns true if this region is currently visible.
50226      * @return {Boolean}
50227      */
50228     isVisible : function(){
50229         return this.visible;
50230     },
50231
50232     /**
50233      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50234      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50235      */
50236     setCollapsedTitle : function(title){
50237         title = title || "&#160;";
50238         if(this.collapsedTitleTextEl){
50239             this.collapsedTitleTextEl.innerHTML = title;
50240         }
50241     },
50242
50243     getBox : function(){
50244         var b;
50245         if(!this.collapsed){
50246             b = this.el.getBox(false, true);
50247         }else{
50248             b = this.collapsedEl.getBox(false, true);
50249         }
50250         return b;
50251     },
50252
50253     getMargins : function(){
50254         return this.collapsed ? this.cmargins : this.margins;
50255     },
50256
50257     highlight : function(){
50258         this.el.addClass("x-layout-panel-dragover");
50259     },
50260
50261     unhighlight : function(){
50262         this.el.removeClass("x-layout-panel-dragover");
50263     },
50264
50265     updateBox : function(box){
50266         this.box = box;
50267         if(!this.collapsed){
50268             this.el.dom.style.left = box.x + "px";
50269             this.el.dom.style.top = box.y + "px";
50270             this.updateBody(box.width, box.height);
50271         }else{
50272             this.collapsedEl.dom.style.left = box.x + "px";
50273             this.collapsedEl.dom.style.top = box.y + "px";
50274             this.collapsedEl.setSize(box.width, box.height);
50275         }
50276         if(this.tabs){
50277             this.tabs.autoSizeTabs();
50278         }
50279     },
50280
50281     updateBody : function(w, h){
50282         if(w !== null){
50283             this.el.setWidth(w);
50284             w -= this.el.getBorderWidth("rl");
50285             if(this.config.adjustments){
50286                 w += this.config.adjustments[0];
50287             }
50288         }
50289         if(h !== null){
50290             this.el.setHeight(h);
50291             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50292             h -= this.el.getBorderWidth("tb");
50293             if(this.config.adjustments){
50294                 h += this.config.adjustments[1];
50295             }
50296             this.bodyEl.setHeight(h);
50297             if(this.tabs){
50298                 h = this.tabs.syncHeight(h);
50299             }
50300         }
50301         if(this.panelSize){
50302             w = w !== null ? w : this.panelSize.width;
50303             h = h !== null ? h : this.panelSize.height;
50304         }
50305         if(this.activePanel){
50306             var el = this.activePanel.getEl();
50307             w = w !== null ? w : el.getWidth();
50308             h = h !== null ? h : el.getHeight();
50309             this.panelSize = {width: w, height: h};
50310             this.activePanel.setSize(w, h);
50311         }
50312         if(Roo.isIE && this.tabs){
50313             this.tabs.el.repaint();
50314         }
50315     },
50316
50317     /**
50318      * Returns the container element for this region.
50319      * @return {Roo.Element}
50320      */
50321     getEl : function(){
50322         return this.el;
50323     },
50324
50325     /**
50326      * Hides this region.
50327      */
50328     hide : function(){
50329         if(!this.collapsed){
50330             this.el.dom.style.left = "-2000px";
50331             this.el.hide();
50332         }else{
50333             this.collapsedEl.dom.style.left = "-2000px";
50334             this.collapsedEl.hide();
50335         }
50336         this.visible = false;
50337         this.fireEvent("visibilitychange", this, false);
50338     },
50339
50340     /**
50341      * Shows this region if it was previously hidden.
50342      */
50343     show : function(){
50344         if(!this.collapsed){
50345             this.el.show();
50346         }else{
50347             this.collapsedEl.show();
50348         }
50349         this.visible = true;
50350         this.fireEvent("visibilitychange", this, true);
50351     },
50352
50353     closeClicked : function(){
50354         if(this.activePanel){
50355             this.remove(this.activePanel);
50356         }
50357     },
50358
50359     collapseClick : function(e){
50360         if(this.isSlid){
50361            e.stopPropagation();
50362            this.slideIn();
50363         }else{
50364            e.stopPropagation();
50365            this.slideOut();
50366         }
50367     },
50368
50369     /**
50370      * Collapses this region.
50371      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50372      */
50373     collapse : function(skipAnim){
50374         if(this.collapsed) return;
50375         this.collapsed = true;
50376         if(this.split){
50377             this.split.el.hide();
50378         }
50379         if(this.config.animate && skipAnim !== true){
50380             this.fireEvent("invalidated", this);
50381             this.animateCollapse();
50382         }else{
50383             this.el.setLocation(-20000,-20000);
50384             this.el.hide();
50385             this.collapsedEl.show();
50386             this.fireEvent("collapsed", this);
50387             this.fireEvent("invalidated", this);
50388         }
50389     },
50390
50391     animateCollapse : function(){
50392         // overridden
50393     },
50394
50395     /**
50396      * Expands this region if it was previously collapsed.
50397      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50398      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50399      */
50400     expand : function(e, skipAnim){
50401         if(e) e.stopPropagation();
50402         if(!this.collapsed || this.el.hasActiveFx()) return;
50403         if(this.isSlid){
50404             this.afterSlideIn();
50405             skipAnim = true;
50406         }
50407         this.collapsed = false;
50408         if(this.config.animate && skipAnim !== true){
50409             this.animateExpand();
50410         }else{
50411             this.el.show();
50412             if(this.split){
50413                 this.split.el.show();
50414             }
50415             this.collapsedEl.setLocation(-2000,-2000);
50416             this.collapsedEl.hide();
50417             this.fireEvent("invalidated", this);
50418             this.fireEvent("expanded", this);
50419         }
50420     },
50421
50422     animateExpand : function(){
50423         // overridden
50424     },
50425
50426     initTabs : function()
50427     {
50428         this.bodyEl.setStyle("overflow", "hidden");
50429         var ts = new Roo.TabPanel(
50430                 this.bodyEl.dom,
50431                 {
50432                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50433                     disableTooltips: this.config.disableTabTips,
50434                     toolbar : this.config.toolbar
50435                 }
50436         );
50437         if(this.config.hideTabs){
50438             ts.stripWrap.setDisplayed(false);
50439         }
50440         this.tabs = ts;
50441         ts.resizeTabs = this.config.resizeTabs === true;
50442         ts.minTabWidth = this.config.minTabWidth || 40;
50443         ts.maxTabWidth = this.config.maxTabWidth || 250;
50444         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50445         ts.monitorResize = false;
50446         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50447         ts.bodyEl.addClass('x-layout-tabs-body');
50448         this.panels.each(this.initPanelAsTab, this);
50449     },
50450
50451     initPanelAsTab : function(panel){
50452         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50453                     this.config.closeOnTab && panel.isClosable());
50454         if(panel.tabTip !== undefined){
50455             ti.setTooltip(panel.tabTip);
50456         }
50457         ti.on("activate", function(){
50458               this.setActivePanel(panel);
50459         }, this);
50460         if(this.config.closeOnTab){
50461             ti.on("beforeclose", function(t, e){
50462                 e.cancel = true;
50463                 this.remove(panel);
50464             }, this);
50465         }
50466         return ti;
50467     },
50468
50469     updatePanelTitle : function(panel, title){
50470         if(this.activePanel == panel){
50471             this.updateTitle(title);
50472         }
50473         if(this.tabs){
50474             var ti = this.tabs.getTab(panel.getEl().id);
50475             ti.setText(title);
50476             if(panel.tabTip !== undefined){
50477                 ti.setTooltip(panel.tabTip);
50478             }
50479         }
50480     },
50481
50482     updateTitle : function(title){
50483         if(this.titleTextEl && !this.config.title){
50484             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50485         }
50486     },
50487
50488     setActivePanel : function(panel){
50489         panel = this.getPanel(panel);
50490         if(this.activePanel && this.activePanel != panel){
50491             this.activePanel.setActiveState(false);
50492         }
50493         this.activePanel = panel;
50494         panel.setActiveState(true);
50495         if(this.panelSize){
50496             panel.setSize(this.panelSize.width, this.panelSize.height);
50497         }
50498         if(this.closeBtn){
50499             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50500         }
50501         this.updateTitle(panel.getTitle());
50502         if(this.tabs){
50503             this.fireEvent("invalidated", this);
50504         }
50505         this.fireEvent("panelactivated", this, panel);
50506     },
50507
50508     /**
50509      * Shows the specified panel.
50510      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50511      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50512      */
50513     showPanel : function(panel)
50514     {
50515         panel = this.getPanel(panel);
50516         if(panel){
50517             if(this.tabs){
50518                 var tab = this.tabs.getTab(panel.getEl().id);
50519                 if(tab.isHidden()){
50520                     this.tabs.unhideTab(tab.id);
50521                 }
50522                 tab.activate();
50523             }else{
50524                 this.setActivePanel(panel);
50525             }
50526         }
50527         return panel;
50528     },
50529
50530     /**
50531      * Get the active panel for this region.
50532      * @return {Roo.ContentPanel} The active panel or null
50533      */
50534     getActivePanel : function(){
50535         return this.activePanel;
50536     },
50537
50538     validateVisibility : function(){
50539         if(this.panels.getCount() < 1){
50540             this.updateTitle("&#160;");
50541             this.closeBtn.hide();
50542             this.hide();
50543         }else{
50544             if(!this.isVisible()){
50545                 this.show();
50546             }
50547         }
50548     },
50549
50550     /**
50551      * Adds the passed ContentPanel(s) to this region.
50552      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50553      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50554      */
50555     add : function(panel){
50556         if(arguments.length > 1){
50557             for(var i = 0, len = arguments.length; i < len; i++) {
50558                 this.add(arguments[i]);
50559             }
50560             return null;
50561         }
50562         if(this.hasPanel(panel)){
50563             this.showPanel(panel);
50564             return panel;
50565         }
50566         panel.setRegion(this);
50567         this.panels.add(panel);
50568         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50569             this.bodyEl.dom.appendChild(panel.getEl().dom);
50570             if(panel.background !== true){
50571                 this.setActivePanel(panel);
50572             }
50573             this.fireEvent("paneladded", this, panel);
50574             return panel;
50575         }
50576         if(!this.tabs){
50577             this.initTabs();
50578         }else{
50579             this.initPanelAsTab(panel);
50580         }
50581         if(panel.background !== true){
50582             this.tabs.activate(panel.getEl().id);
50583         }
50584         this.fireEvent("paneladded", this, panel);
50585         return panel;
50586     },
50587
50588     /**
50589      * Hides the tab for the specified panel.
50590      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50591      */
50592     hidePanel : function(panel){
50593         if(this.tabs && (panel = this.getPanel(panel))){
50594             this.tabs.hideTab(panel.getEl().id);
50595         }
50596     },
50597
50598     /**
50599      * Unhides the tab for a previously hidden panel.
50600      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50601      */
50602     unhidePanel : function(panel){
50603         if(this.tabs && (panel = this.getPanel(panel))){
50604             this.tabs.unhideTab(panel.getEl().id);
50605         }
50606     },
50607
50608     clearPanels : function(){
50609         while(this.panels.getCount() > 0){
50610              this.remove(this.panels.first());
50611         }
50612     },
50613
50614     /**
50615      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50616      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50617      * @param {Boolean} preservePanel Overrides the config preservePanel option
50618      * @return {Roo.ContentPanel} The panel that was removed
50619      */
50620     remove : function(panel, preservePanel){
50621         panel = this.getPanel(panel);
50622         if(!panel){
50623             return null;
50624         }
50625         var e = {};
50626         this.fireEvent("beforeremove", this, panel, e);
50627         if(e.cancel === true){
50628             return null;
50629         }
50630         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50631         var panelId = panel.getId();
50632         this.panels.removeKey(panelId);
50633         if(preservePanel){
50634             document.body.appendChild(panel.getEl().dom);
50635         }
50636         if(this.tabs){
50637             this.tabs.removeTab(panel.getEl().id);
50638         }else if (!preservePanel){
50639             this.bodyEl.dom.removeChild(panel.getEl().dom);
50640         }
50641         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50642             var p = this.panels.first();
50643             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50644             tempEl.appendChild(p.getEl().dom);
50645             this.bodyEl.update("");
50646             this.bodyEl.dom.appendChild(p.getEl().dom);
50647             tempEl = null;
50648             this.updateTitle(p.getTitle());
50649             this.tabs = null;
50650             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50651             this.setActivePanel(p);
50652         }
50653         panel.setRegion(null);
50654         if(this.activePanel == panel){
50655             this.activePanel = null;
50656         }
50657         if(this.config.autoDestroy !== false && preservePanel !== true){
50658             try{panel.destroy();}catch(e){}
50659         }
50660         this.fireEvent("panelremoved", this, panel);
50661         return panel;
50662     },
50663
50664     /**
50665      * Returns the TabPanel component used by this region
50666      * @return {Roo.TabPanel}
50667      */
50668     getTabs : function(){
50669         return this.tabs;
50670     },
50671
50672     createTool : function(parentEl, className){
50673         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50674             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50675         btn.addClassOnOver("x-layout-tools-button-over");
50676         return btn;
50677     }
50678 });/*
50679  * Based on:
50680  * Ext JS Library 1.1.1
50681  * Copyright(c) 2006-2007, Ext JS, LLC.
50682  *
50683  * Originally Released Under LGPL - original licence link has changed is not relivant.
50684  *
50685  * Fork - LGPL
50686  * <script type="text/javascript">
50687  */
50688  
50689
50690
50691 /**
50692  * @class Roo.SplitLayoutRegion
50693  * @extends Roo.LayoutRegion
50694  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50695  */
50696 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50697     this.cursor = cursor;
50698     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50699 };
50700
50701 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50702     splitTip : "Drag to resize.",
50703     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50704     useSplitTips : false,
50705
50706     applyConfig : function(config){
50707         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50708         if(config.split){
50709             if(!this.split){
50710                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50711                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50712                 /** The SplitBar for this region 
50713                 * @type Roo.SplitBar */
50714                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50715                 this.split.on("moved", this.onSplitMove, this);
50716                 this.split.useShim = config.useShim === true;
50717                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50718                 if(this.useSplitTips){
50719                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50720                 }
50721                 if(config.collapsible){
50722                     this.split.el.on("dblclick", this.collapse,  this);
50723                 }
50724             }
50725             if(typeof config.minSize != "undefined"){
50726                 this.split.minSize = config.minSize;
50727             }
50728             if(typeof config.maxSize != "undefined"){
50729                 this.split.maxSize = config.maxSize;
50730             }
50731             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50732                 this.hideSplitter();
50733             }
50734         }
50735     },
50736
50737     getHMaxSize : function(){
50738          var cmax = this.config.maxSize || 10000;
50739          var center = this.mgr.getRegion("center");
50740          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50741     },
50742
50743     getVMaxSize : function(){
50744          var cmax = this.config.maxSize || 10000;
50745          var center = this.mgr.getRegion("center");
50746          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50747     },
50748
50749     onSplitMove : function(split, newSize){
50750         this.fireEvent("resized", this, newSize);
50751     },
50752     
50753     /** 
50754      * Returns the {@link Roo.SplitBar} for this region.
50755      * @return {Roo.SplitBar}
50756      */
50757     getSplitBar : function(){
50758         return this.split;
50759     },
50760     
50761     hide : function(){
50762         this.hideSplitter();
50763         Roo.SplitLayoutRegion.superclass.hide.call(this);
50764     },
50765
50766     hideSplitter : function(){
50767         if(this.split){
50768             this.split.el.setLocation(-2000,-2000);
50769             this.split.el.hide();
50770         }
50771     },
50772
50773     show : function(){
50774         if(this.split){
50775             this.split.el.show();
50776         }
50777         Roo.SplitLayoutRegion.superclass.show.call(this);
50778     },
50779     
50780     beforeSlide: function(){
50781         if(Roo.isGecko){// firefox overflow auto bug workaround
50782             this.bodyEl.clip();
50783             if(this.tabs) this.tabs.bodyEl.clip();
50784             if(this.activePanel){
50785                 this.activePanel.getEl().clip();
50786                 
50787                 if(this.activePanel.beforeSlide){
50788                     this.activePanel.beforeSlide();
50789                 }
50790             }
50791         }
50792     },
50793     
50794     afterSlide : function(){
50795         if(Roo.isGecko){// firefox overflow auto bug workaround
50796             this.bodyEl.unclip();
50797             if(this.tabs) this.tabs.bodyEl.unclip();
50798             if(this.activePanel){
50799                 this.activePanel.getEl().unclip();
50800                 if(this.activePanel.afterSlide){
50801                     this.activePanel.afterSlide();
50802                 }
50803             }
50804         }
50805     },
50806
50807     initAutoHide : function(){
50808         if(this.autoHide !== false){
50809             if(!this.autoHideHd){
50810                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50811                 this.autoHideHd = {
50812                     "mouseout": function(e){
50813                         if(!e.within(this.el, true)){
50814                             st.delay(500);
50815                         }
50816                     },
50817                     "mouseover" : function(e){
50818                         st.cancel();
50819                     },
50820                     scope : this
50821                 };
50822             }
50823             this.el.on(this.autoHideHd);
50824         }
50825     },
50826
50827     clearAutoHide : function(){
50828         if(this.autoHide !== false){
50829             this.el.un("mouseout", this.autoHideHd.mouseout);
50830             this.el.un("mouseover", this.autoHideHd.mouseover);
50831         }
50832     },
50833
50834     clearMonitor : function(){
50835         Roo.get(document).un("click", this.slideInIf, this);
50836     },
50837
50838     // these names are backwards but not changed for compat
50839     slideOut : function(){
50840         if(this.isSlid || this.el.hasActiveFx()){
50841             return;
50842         }
50843         this.isSlid = true;
50844         if(this.collapseBtn){
50845             this.collapseBtn.hide();
50846         }
50847         this.closeBtnState = this.closeBtn.getStyle('display');
50848         this.closeBtn.hide();
50849         if(this.stickBtn){
50850             this.stickBtn.show();
50851         }
50852         this.el.show();
50853         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50854         this.beforeSlide();
50855         this.el.setStyle("z-index", 10001);
50856         this.el.slideIn(this.getSlideAnchor(), {
50857             callback: function(){
50858                 this.afterSlide();
50859                 this.initAutoHide();
50860                 Roo.get(document).on("click", this.slideInIf, this);
50861                 this.fireEvent("slideshow", this);
50862             },
50863             scope: this,
50864             block: true
50865         });
50866     },
50867
50868     afterSlideIn : function(){
50869         this.clearAutoHide();
50870         this.isSlid = false;
50871         this.clearMonitor();
50872         this.el.setStyle("z-index", "");
50873         if(this.collapseBtn){
50874             this.collapseBtn.show();
50875         }
50876         this.closeBtn.setStyle('display', this.closeBtnState);
50877         if(this.stickBtn){
50878             this.stickBtn.hide();
50879         }
50880         this.fireEvent("slidehide", this);
50881     },
50882
50883     slideIn : function(cb){
50884         if(!this.isSlid || this.el.hasActiveFx()){
50885             Roo.callback(cb);
50886             return;
50887         }
50888         this.isSlid = false;
50889         this.beforeSlide();
50890         this.el.slideOut(this.getSlideAnchor(), {
50891             callback: function(){
50892                 this.el.setLeftTop(-10000, -10000);
50893                 this.afterSlide();
50894                 this.afterSlideIn();
50895                 Roo.callback(cb);
50896             },
50897             scope: this,
50898             block: true
50899         });
50900     },
50901     
50902     slideInIf : function(e){
50903         if(!e.within(this.el)){
50904             this.slideIn();
50905         }
50906     },
50907
50908     animateCollapse : function(){
50909         this.beforeSlide();
50910         this.el.setStyle("z-index", 20000);
50911         var anchor = this.getSlideAnchor();
50912         this.el.slideOut(anchor, {
50913             callback : function(){
50914                 this.el.setStyle("z-index", "");
50915                 this.collapsedEl.slideIn(anchor, {duration:.3});
50916                 this.afterSlide();
50917                 this.el.setLocation(-10000,-10000);
50918                 this.el.hide();
50919                 this.fireEvent("collapsed", this);
50920             },
50921             scope: this,
50922             block: true
50923         });
50924     },
50925
50926     animateExpand : function(){
50927         this.beforeSlide();
50928         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50929         this.el.setStyle("z-index", 20000);
50930         this.collapsedEl.hide({
50931             duration:.1
50932         });
50933         this.el.slideIn(this.getSlideAnchor(), {
50934             callback : function(){
50935                 this.el.setStyle("z-index", "");
50936                 this.afterSlide();
50937                 if(this.split){
50938                     this.split.el.show();
50939                 }
50940                 this.fireEvent("invalidated", this);
50941                 this.fireEvent("expanded", this);
50942             },
50943             scope: this,
50944             block: true
50945         });
50946     },
50947
50948     anchors : {
50949         "west" : "left",
50950         "east" : "right",
50951         "north" : "top",
50952         "south" : "bottom"
50953     },
50954
50955     sanchors : {
50956         "west" : "l",
50957         "east" : "r",
50958         "north" : "t",
50959         "south" : "b"
50960     },
50961
50962     canchors : {
50963         "west" : "tl-tr",
50964         "east" : "tr-tl",
50965         "north" : "tl-bl",
50966         "south" : "bl-tl"
50967     },
50968
50969     getAnchor : function(){
50970         return this.anchors[this.position];
50971     },
50972
50973     getCollapseAnchor : function(){
50974         return this.canchors[this.position];
50975     },
50976
50977     getSlideAnchor : function(){
50978         return this.sanchors[this.position];
50979     },
50980
50981     getAlignAdj : function(){
50982         var cm = this.cmargins;
50983         switch(this.position){
50984             case "west":
50985                 return [0, 0];
50986             break;
50987             case "east":
50988                 return [0, 0];
50989             break;
50990             case "north":
50991                 return [0, 0];
50992             break;
50993             case "south":
50994                 return [0, 0];
50995             break;
50996         }
50997     },
50998
50999     getExpandAdj : function(){
51000         var c = this.collapsedEl, cm = this.cmargins;
51001         switch(this.position){
51002             case "west":
51003                 return [-(cm.right+c.getWidth()+cm.left), 0];
51004             break;
51005             case "east":
51006                 return [cm.right+c.getWidth()+cm.left, 0];
51007             break;
51008             case "north":
51009                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51010             break;
51011             case "south":
51012                 return [0, cm.top+cm.bottom+c.getHeight()];
51013             break;
51014         }
51015     }
51016 });/*
51017  * Based on:
51018  * Ext JS Library 1.1.1
51019  * Copyright(c) 2006-2007, Ext JS, LLC.
51020  *
51021  * Originally Released Under LGPL - original licence link has changed is not relivant.
51022  *
51023  * Fork - LGPL
51024  * <script type="text/javascript">
51025  */
51026 /*
51027  * These classes are private internal classes
51028  */
51029 Roo.CenterLayoutRegion = function(mgr, config){
51030     Roo.LayoutRegion.call(this, mgr, config, "center");
51031     this.visible = true;
51032     this.minWidth = config.minWidth || 20;
51033     this.minHeight = config.minHeight || 20;
51034 };
51035
51036 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51037     hide : function(){
51038         // center panel can't be hidden
51039     },
51040     
51041     show : function(){
51042         // center panel can't be hidden
51043     },
51044     
51045     getMinWidth: function(){
51046         return this.minWidth;
51047     },
51048     
51049     getMinHeight: function(){
51050         return this.minHeight;
51051     }
51052 });
51053
51054
51055 Roo.NorthLayoutRegion = function(mgr, config){
51056     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51057     if(this.split){
51058         this.split.placement = Roo.SplitBar.TOP;
51059         this.split.orientation = Roo.SplitBar.VERTICAL;
51060         this.split.el.addClass("x-layout-split-v");
51061     }
51062     var size = config.initialSize || config.height;
51063     if(typeof size != "undefined"){
51064         this.el.setHeight(size);
51065     }
51066 };
51067 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51068     orientation: Roo.SplitBar.VERTICAL,
51069     getBox : function(){
51070         if(this.collapsed){
51071             return this.collapsedEl.getBox();
51072         }
51073         var box = this.el.getBox();
51074         if(this.split){
51075             box.height += this.split.el.getHeight();
51076         }
51077         return box;
51078     },
51079     
51080     updateBox : function(box){
51081         if(this.split && !this.collapsed){
51082             box.height -= this.split.el.getHeight();
51083             this.split.el.setLeft(box.x);
51084             this.split.el.setTop(box.y+box.height);
51085             this.split.el.setWidth(box.width);
51086         }
51087         if(this.collapsed){
51088             this.updateBody(box.width, null);
51089         }
51090         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51091     }
51092 });
51093
51094 Roo.SouthLayoutRegion = function(mgr, config){
51095     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51096     if(this.split){
51097         this.split.placement = Roo.SplitBar.BOTTOM;
51098         this.split.orientation = Roo.SplitBar.VERTICAL;
51099         this.split.el.addClass("x-layout-split-v");
51100     }
51101     var size = config.initialSize || config.height;
51102     if(typeof size != "undefined"){
51103         this.el.setHeight(size);
51104     }
51105 };
51106 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51107     orientation: Roo.SplitBar.VERTICAL,
51108     getBox : function(){
51109         if(this.collapsed){
51110             return this.collapsedEl.getBox();
51111         }
51112         var box = this.el.getBox();
51113         if(this.split){
51114             var sh = this.split.el.getHeight();
51115             box.height += sh;
51116             box.y -= sh;
51117         }
51118         return box;
51119     },
51120     
51121     updateBox : function(box){
51122         if(this.split && !this.collapsed){
51123             var sh = this.split.el.getHeight();
51124             box.height -= sh;
51125             box.y += sh;
51126             this.split.el.setLeft(box.x);
51127             this.split.el.setTop(box.y-sh);
51128             this.split.el.setWidth(box.width);
51129         }
51130         if(this.collapsed){
51131             this.updateBody(box.width, null);
51132         }
51133         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51134     }
51135 });
51136
51137 Roo.EastLayoutRegion = function(mgr, config){
51138     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51139     if(this.split){
51140         this.split.placement = Roo.SplitBar.RIGHT;
51141         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51142         this.split.el.addClass("x-layout-split-h");
51143     }
51144     var size = config.initialSize || config.width;
51145     if(typeof size != "undefined"){
51146         this.el.setWidth(size);
51147     }
51148 };
51149 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51150     orientation: Roo.SplitBar.HORIZONTAL,
51151     getBox : function(){
51152         if(this.collapsed){
51153             return this.collapsedEl.getBox();
51154         }
51155         var box = this.el.getBox();
51156         if(this.split){
51157             var sw = this.split.el.getWidth();
51158             box.width += sw;
51159             box.x -= sw;
51160         }
51161         return box;
51162     },
51163
51164     updateBox : function(box){
51165         if(this.split && !this.collapsed){
51166             var sw = this.split.el.getWidth();
51167             box.width -= sw;
51168             this.split.el.setLeft(box.x);
51169             this.split.el.setTop(box.y);
51170             this.split.el.setHeight(box.height);
51171             box.x += sw;
51172         }
51173         if(this.collapsed){
51174             this.updateBody(null, box.height);
51175         }
51176         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51177     }
51178 });
51179
51180 Roo.WestLayoutRegion = function(mgr, config){
51181     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51182     if(this.split){
51183         this.split.placement = Roo.SplitBar.LEFT;
51184         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51185         this.split.el.addClass("x-layout-split-h");
51186     }
51187     var size = config.initialSize || config.width;
51188     if(typeof size != "undefined"){
51189         this.el.setWidth(size);
51190     }
51191 };
51192 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51193     orientation: Roo.SplitBar.HORIZONTAL,
51194     getBox : function(){
51195         if(this.collapsed){
51196             return this.collapsedEl.getBox();
51197         }
51198         var box = this.el.getBox();
51199         if(this.split){
51200             box.width += this.split.el.getWidth();
51201         }
51202         return box;
51203     },
51204     
51205     updateBox : function(box){
51206         if(this.split && !this.collapsed){
51207             var sw = this.split.el.getWidth();
51208             box.width -= sw;
51209             this.split.el.setLeft(box.x+box.width);
51210             this.split.el.setTop(box.y);
51211             this.split.el.setHeight(box.height);
51212         }
51213         if(this.collapsed){
51214             this.updateBody(null, box.height);
51215         }
51216         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51217     }
51218 });
51219 /*
51220  * Based on:
51221  * Ext JS Library 1.1.1
51222  * Copyright(c) 2006-2007, Ext JS, LLC.
51223  *
51224  * Originally Released Under LGPL - original licence link has changed is not relivant.
51225  *
51226  * Fork - LGPL
51227  * <script type="text/javascript">
51228  */
51229  
51230  
51231 /*
51232  * Private internal class for reading and applying state
51233  */
51234 Roo.LayoutStateManager = function(layout){
51235      // default empty state
51236      this.state = {
51237         north: {},
51238         south: {},
51239         east: {},
51240         west: {}       
51241     };
51242 };
51243
51244 Roo.LayoutStateManager.prototype = {
51245     init : function(layout, provider){
51246         this.provider = provider;
51247         var state = provider.get(layout.id+"-layout-state");
51248         if(state){
51249             var wasUpdating = layout.isUpdating();
51250             if(!wasUpdating){
51251                 layout.beginUpdate();
51252             }
51253             for(var key in state){
51254                 if(typeof state[key] != "function"){
51255                     var rstate = state[key];
51256                     var r = layout.getRegion(key);
51257                     if(r && rstate){
51258                         if(rstate.size){
51259                             r.resizeTo(rstate.size);
51260                         }
51261                         if(rstate.collapsed == true){
51262                             r.collapse(true);
51263                         }else{
51264                             r.expand(null, true);
51265                         }
51266                     }
51267                 }
51268             }
51269             if(!wasUpdating){
51270                 layout.endUpdate();
51271             }
51272             this.state = state; 
51273         }
51274         this.layout = layout;
51275         layout.on("regionresized", this.onRegionResized, this);
51276         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51277         layout.on("regionexpanded", this.onRegionExpanded, this);
51278     },
51279     
51280     storeState : function(){
51281         this.provider.set(this.layout.id+"-layout-state", this.state);
51282     },
51283     
51284     onRegionResized : function(region, newSize){
51285         this.state[region.getPosition()].size = newSize;
51286         this.storeState();
51287     },
51288     
51289     onRegionCollapsed : function(region){
51290         this.state[region.getPosition()].collapsed = true;
51291         this.storeState();
51292     },
51293     
51294     onRegionExpanded : function(region){
51295         this.state[region.getPosition()].collapsed = false;
51296         this.storeState();
51297     }
51298 };/*
51299  * Based on:
51300  * Ext JS Library 1.1.1
51301  * Copyright(c) 2006-2007, Ext JS, LLC.
51302  *
51303  * Originally Released Under LGPL - original licence link has changed is not relivant.
51304  *
51305  * Fork - LGPL
51306  * <script type="text/javascript">
51307  */
51308 /**
51309  * @class Roo.ContentPanel
51310  * @extends Roo.util.Observable
51311  * A basic ContentPanel element.
51312  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51313  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51314  * @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
51315  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51316  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51317  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51318  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51319  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51320  * @cfg {String} title          The title for this panel
51321  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51322  * @cfg {String} url            Calls {@link #setUrl} with this value
51323  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51324  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51325  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51326  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51327
51328  * @constructor
51329  * Create a new ContentPanel.
51330  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51331  * @param {String/Object} config A string to set only the title or a config object
51332  * @param {String} content (optional) Set the HTML content for this panel
51333  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51334  */
51335 Roo.ContentPanel = function(el, config, content){
51336     
51337      
51338     /*
51339     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51340         config = el;
51341         el = Roo.id();
51342     }
51343     if (config && config.parentLayout) { 
51344         el = config.parentLayout.el.createChild(); 
51345     }
51346     */
51347     if(el.autoCreate){ // xtype is available if this is called from factory
51348         config = el;
51349         el = Roo.id();
51350     }
51351     this.el = Roo.get(el);
51352     if(!this.el && config && config.autoCreate){
51353         if(typeof config.autoCreate == "object"){
51354             if(!config.autoCreate.id){
51355                 config.autoCreate.id = config.id||el;
51356             }
51357             this.el = Roo.DomHelper.append(document.body,
51358                         config.autoCreate, true);
51359         }else{
51360             this.el = Roo.DomHelper.append(document.body,
51361                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51362         }
51363     }
51364     this.closable = false;
51365     this.loaded = false;
51366     this.active = false;
51367     if(typeof config == "string"){
51368         this.title = config;
51369     }else{
51370         Roo.apply(this, config);
51371     }
51372     
51373     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51374         this.wrapEl = this.el.wrap();
51375         this.toolbar.container = this.el.insertSibling(false, 'before');
51376         this.toolbar = new Roo.Toolbar(this.toolbar);
51377     }
51378     
51379     // xtype created footer. - not sure if will work as we normally have to render first..
51380     if (this.footer && !this.footer.el && this.footer.xtype) {
51381         if (!this.wrapEl) {
51382             this.wrapEl = this.el.wrap();
51383         }
51384     
51385         this.footer.container = this.wrapEl.createChild();
51386          
51387         this.footer = Roo.factory(this.footer, Roo);
51388         
51389     }
51390     
51391     if(this.resizeEl){
51392         this.resizeEl = Roo.get(this.resizeEl, true);
51393     }else{
51394         this.resizeEl = this.el;
51395     }
51396     // handle view.xtype
51397     
51398  
51399     
51400     
51401     this.addEvents({
51402         /**
51403          * @event activate
51404          * Fires when this panel is activated. 
51405          * @param {Roo.ContentPanel} this
51406          */
51407         "activate" : true,
51408         /**
51409          * @event deactivate
51410          * Fires when this panel is activated. 
51411          * @param {Roo.ContentPanel} this
51412          */
51413         "deactivate" : true,
51414
51415         /**
51416          * @event resize
51417          * Fires when this panel is resized if fitToFrame is true.
51418          * @param {Roo.ContentPanel} this
51419          * @param {Number} width The width after any component adjustments
51420          * @param {Number} height The height after any component adjustments
51421          */
51422         "resize" : true,
51423         
51424          /**
51425          * @event render
51426          * Fires when this tab is created
51427          * @param {Roo.ContentPanel} this
51428          */
51429         "render" : true
51430         
51431         
51432         
51433     });
51434     
51435
51436     
51437     
51438     if(this.autoScroll){
51439         this.resizeEl.setStyle("overflow", "auto");
51440     } else {
51441         // fix randome scrolling
51442         this.el.on('scroll', function() {
51443             Roo.log('fix random scolling');
51444             this.scrollTo('top',0); 
51445         });
51446     }
51447     content = content || this.content;
51448     if(content){
51449         this.setContent(content);
51450     }
51451     if(config && config.url){
51452         this.setUrl(this.url, this.params, this.loadOnce);
51453     }
51454     
51455     
51456     
51457     Roo.ContentPanel.superclass.constructor.call(this);
51458     
51459     if (this.view && typeof(this.view.xtype) != 'undefined') {
51460         this.view.el = this.el.appendChild(document.createElement("div"));
51461         this.view = Roo.factory(this.view); 
51462         this.view.render  &&  this.view.render(false, '');  
51463     }
51464     
51465     
51466     this.fireEvent('render', this);
51467 };
51468
51469 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51470     tabTip:'',
51471     setRegion : function(region){
51472         this.region = region;
51473         if(region){
51474            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51475         }else{
51476            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51477         } 
51478     },
51479     
51480     /**
51481      * Returns the toolbar for this Panel if one was configured. 
51482      * @return {Roo.Toolbar} 
51483      */
51484     getToolbar : function(){
51485         return this.toolbar;
51486     },
51487     
51488     setActiveState : function(active){
51489         this.active = active;
51490         if(!active){
51491             this.fireEvent("deactivate", this);
51492         }else{
51493             this.fireEvent("activate", this);
51494         }
51495     },
51496     /**
51497      * Updates this panel's element
51498      * @param {String} content The new content
51499      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51500     */
51501     setContent : function(content, loadScripts){
51502         this.el.update(content, loadScripts);
51503     },
51504
51505     ignoreResize : function(w, h){
51506         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51507             return true;
51508         }else{
51509             this.lastSize = {width: w, height: h};
51510             return false;
51511         }
51512     },
51513     /**
51514      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51515      * @return {Roo.UpdateManager} The UpdateManager
51516      */
51517     getUpdateManager : function(){
51518         return this.el.getUpdateManager();
51519     },
51520      /**
51521      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51522      * @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:
51523 <pre><code>
51524 panel.load({
51525     url: "your-url.php",
51526     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51527     callback: yourFunction,
51528     scope: yourObject, //(optional scope)
51529     discardUrl: false,
51530     nocache: false,
51531     text: "Loading...",
51532     timeout: 30,
51533     scripts: false
51534 });
51535 </code></pre>
51536      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51537      * 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.
51538      * @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}
51539      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51540      * @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.
51541      * @return {Roo.ContentPanel} this
51542      */
51543     load : function(){
51544         var um = this.el.getUpdateManager();
51545         um.update.apply(um, arguments);
51546         return this;
51547     },
51548
51549
51550     /**
51551      * 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.
51552      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51553      * @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)
51554      * @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)
51555      * @return {Roo.UpdateManager} The UpdateManager
51556      */
51557     setUrl : function(url, params, loadOnce){
51558         if(this.refreshDelegate){
51559             this.removeListener("activate", this.refreshDelegate);
51560         }
51561         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51562         this.on("activate", this.refreshDelegate);
51563         return this.el.getUpdateManager();
51564     },
51565     
51566     _handleRefresh : function(url, params, loadOnce){
51567         if(!loadOnce || !this.loaded){
51568             var updater = this.el.getUpdateManager();
51569             updater.update(url, params, this._setLoaded.createDelegate(this));
51570         }
51571     },
51572     
51573     _setLoaded : function(){
51574         this.loaded = true;
51575     }, 
51576     
51577     /**
51578      * Returns this panel's id
51579      * @return {String} 
51580      */
51581     getId : function(){
51582         return this.el.id;
51583     },
51584     
51585     /** 
51586      * Returns this panel's element - used by regiosn to add.
51587      * @return {Roo.Element} 
51588      */
51589     getEl : function(){
51590         return this.wrapEl || this.el;
51591     },
51592     
51593     adjustForComponents : function(width, height)
51594     {
51595         //Roo.log('adjustForComponents ');
51596         if(this.resizeEl != this.el){
51597             width -= this.el.getFrameWidth('lr');
51598             height -= this.el.getFrameWidth('tb');
51599         }
51600         if(this.toolbar){
51601             var te = this.toolbar.getEl();
51602             height -= te.getHeight();
51603             te.setWidth(width);
51604         }
51605         if(this.footer){
51606             var te = this.footer.getEl();
51607             Roo.log("footer:" + te.getHeight());
51608             
51609             height -= te.getHeight();
51610             te.setWidth(width);
51611         }
51612         
51613         
51614         if(this.adjustments){
51615             width += this.adjustments[0];
51616             height += this.adjustments[1];
51617         }
51618         return {"width": width, "height": height};
51619     },
51620     
51621     setSize : function(width, height){
51622         if(this.fitToFrame && !this.ignoreResize(width, height)){
51623             if(this.fitContainer && this.resizeEl != this.el){
51624                 this.el.setSize(width, height);
51625             }
51626             var size = this.adjustForComponents(width, height);
51627             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51628             this.fireEvent('resize', this, size.width, size.height);
51629         }
51630     },
51631     
51632     /**
51633      * Returns this panel's title
51634      * @return {String} 
51635      */
51636     getTitle : function(){
51637         return this.title;
51638     },
51639     
51640     /**
51641      * Set this panel's title
51642      * @param {String} title
51643      */
51644     setTitle : function(title){
51645         this.title = title;
51646         if(this.region){
51647             this.region.updatePanelTitle(this, title);
51648         }
51649     },
51650     
51651     /**
51652      * Returns true is this panel was configured to be closable
51653      * @return {Boolean} 
51654      */
51655     isClosable : function(){
51656         return this.closable;
51657     },
51658     
51659     beforeSlide : function(){
51660         this.el.clip();
51661         this.resizeEl.clip();
51662     },
51663     
51664     afterSlide : function(){
51665         this.el.unclip();
51666         this.resizeEl.unclip();
51667     },
51668     
51669     /**
51670      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51671      *   Will fail silently if the {@link #setUrl} method has not been called.
51672      *   This does not activate the panel, just updates its content.
51673      */
51674     refresh : function(){
51675         if(this.refreshDelegate){
51676            this.loaded = false;
51677            this.refreshDelegate();
51678         }
51679     },
51680     
51681     /**
51682      * Destroys this panel
51683      */
51684     destroy : function(){
51685         this.el.removeAllListeners();
51686         var tempEl = document.createElement("span");
51687         tempEl.appendChild(this.el.dom);
51688         tempEl.innerHTML = "";
51689         this.el.remove();
51690         this.el = null;
51691     },
51692     
51693     /**
51694      * form - if the content panel contains a form - this is a reference to it.
51695      * @type {Roo.form.Form}
51696      */
51697     form : false,
51698     /**
51699      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51700      *    This contains a reference to it.
51701      * @type {Roo.View}
51702      */
51703     view : false,
51704     
51705       /**
51706      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51707      * <pre><code>
51708
51709 layout.addxtype({
51710        xtype : 'Form',
51711        items: [ .... ]
51712    }
51713 );
51714
51715 </code></pre>
51716      * @param {Object} cfg Xtype definition of item to add.
51717      */
51718     
51719     addxtype : function(cfg) {
51720         // add form..
51721         if (cfg.xtype.match(/^Form$/)) {
51722             
51723             var el;
51724             //if (this.footer) {
51725             //    el = this.footer.container.insertSibling(false, 'before');
51726             //} else {
51727                 el = this.el.createChild();
51728             //}
51729
51730             this.form = new  Roo.form.Form(cfg);
51731             
51732             
51733             if ( this.form.allItems.length) this.form.render(el.dom);
51734             return this.form;
51735         }
51736         // should only have one of theses..
51737         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51738             // views.. should not be just added - used named prop 'view''
51739             
51740             cfg.el = this.el.appendChild(document.createElement("div"));
51741             // factory?
51742             
51743             var ret = new Roo.factory(cfg);
51744              
51745              ret.render && ret.render(false, ''); // render blank..
51746             this.view = ret;
51747             return ret;
51748         }
51749         return false;
51750     }
51751 });
51752
51753 /**
51754  * @class Roo.GridPanel
51755  * @extends Roo.ContentPanel
51756  * @constructor
51757  * Create a new GridPanel.
51758  * @param {Roo.grid.Grid} grid The grid for this panel
51759  * @param {String/Object} config A string to set only the panel's title, or a config object
51760  */
51761 Roo.GridPanel = function(grid, config){
51762     
51763   
51764     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51765         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51766         
51767     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51768     
51769     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51770     
51771     if(this.toolbar){
51772         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51773     }
51774     // xtype created footer. - not sure if will work as we normally have to render first..
51775     if (this.footer && !this.footer.el && this.footer.xtype) {
51776         
51777         this.footer.container = this.grid.getView().getFooterPanel(true);
51778         this.footer.dataSource = this.grid.dataSource;
51779         this.footer = Roo.factory(this.footer, Roo);
51780         
51781     }
51782     
51783     grid.monitorWindowResize = false; // turn off autosizing
51784     grid.autoHeight = false;
51785     grid.autoWidth = false;
51786     this.grid = grid;
51787     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51788 };
51789
51790 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51791     getId : function(){
51792         return this.grid.id;
51793     },
51794     
51795     /**
51796      * Returns the grid for this panel
51797      * @return {Roo.grid.Grid} 
51798      */
51799     getGrid : function(){
51800         return this.grid;    
51801     },
51802     
51803     setSize : function(width, height){
51804         if(!this.ignoreResize(width, height)){
51805             var grid = this.grid;
51806             var size = this.adjustForComponents(width, height);
51807             grid.getGridEl().setSize(size.width, size.height);
51808             grid.autoSize();
51809         }
51810     },
51811     
51812     beforeSlide : function(){
51813         this.grid.getView().scroller.clip();
51814     },
51815     
51816     afterSlide : function(){
51817         this.grid.getView().scroller.unclip();
51818     },
51819     
51820     destroy : function(){
51821         this.grid.destroy();
51822         delete this.grid;
51823         Roo.GridPanel.superclass.destroy.call(this); 
51824     }
51825 });
51826
51827
51828 /**
51829  * @class Roo.NestedLayoutPanel
51830  * @extends Roo.ContentPanel
51831  * @constructor
51832  * Create a new NestedLayoutPanel.
51833  * 
51834  * 
51835  * @param {Roo.BorderLayout} layout The layout for this panel
51836  * @param {String/Object} config A string to set only the title or a config object
51837  */
51838 Roo.NestedLayoutPanel = function(layout, config)
51839 {
51840     // construct with only one argument..
51841     /* FIXME - implement nicer consturctors
51842     if (layout.layout) {
51843         config = layout;
51844         layout = config.layout;
51845         delete config.layout;
51846     }
51847     if (layout.xtype && !layout.getEl) {
51848         // then layout needs constructing..
51849         layout = Roo.factory(layout, Roo);
51850     }
51851     */
51852     
51853     
51854     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51855     
51856     layout.monitorWindowResize = false; // turn off autosizing
51857     this.layout = layout;
51858     this.layout.getEl().addClass("x-layout-nested-layout");
51859     
51860     
51861     
51862     
51863 };
51864
51865 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51866
51867     setSize : function(width, height){
51868         if(!this.ignoreResize(width, height)){
51869             var size = this.adjustForComponents(width, height);
51870             var el = this.layout.getEl();
51871             el.setSize(size.width, size.height);
51872             var touch = el.dom.offsetWidth;
51873             this.layout.layout();
51874             // ie requires a double layout on the first pass
51875             if(Roo.isIE && !this.initialized){
51876                 this.initialized = true;
51877                 this.layout.layout();
51878             }
51879         }
51880     },
51881     
51882     // activate all subpanels if not currently active..
51883     
51884     setActiveState : function(active){
51885         this.active = active;
51886         if(!active){
51887             this.fireEvent("deactivate", this);
51888             return;
51889         }
51890         
51891         this.fireEvent("activate", this);
51892         // not sure if this should happen before or after..
51893         if (!this.layout) {
51894             return; // should not happen..
51895         }
51896         var reg = false;
51897         for (var r in this.layout.regions) {
51898             reg = this.layout.getRegion(r);
51899             if (reg.getActivePanel()) {
51900                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51901                 reg.setActivePanel(reg.getActivePanel());
51902                 continue;
51903             }
51904             if (!reg.panels.length) {
51905                 continue;
51906             }
51907             reg.showPanel(reg.getPanel(0));
51908         }
51909         
51910         
51911         
51912         
51913     },
51914     
51915     /**
51916      * Returns the nested BorderLayout for this panel
51917      * @return {Roo.BorderLayout} 
51918      */
51919     getLayout : function(){
51920         return this.layout;
51921     },
51922     
51923      /**
51924      * Adds a xtype elements to the layout of the nested panel
51925      * <pre><code>
51926
51927 panel.addxtype({
51928        xtype : 'ContentPanel',
51929        region: 'west',
51930        items: [ .... ]
51931    }
51932 );
51933
51934 panel.addxtype({
51935         xtype : 'NestedLayoutPanel',
51936         region: 'west',
51937         layout: {
51938            center: { },
51939            west: { }   
51940         },
51941         items : [ ... list of content panels or nested layout panels.. ]
51942    }
51943 );
51944 </code></pre>
51945      * @param {Object} cfg Xtype definition of item to add.
51946      */
51947     addxtype : function(cfg) {
51948         return this.layout.addxtype(cfg);
51949     
51950     }
51951 });
51952
51953 Roo.ScrollPanel = function(el, config, content){
51954     config = config || {};
51955     config.fitToFrame = true;
51956     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51957     
51958     this.el.dom.style.overflow = "hidden";
51959     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51960     this.el.removeClass("x-layout-inactive-content");
51961     this.el.on("mousewheel", this.onWheel, this);
51962
51963     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51964     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51965     up.unselectable(); down.unselectable();
51966     up.on("click", this.scrollUp, this);
51967     down.on("click", this.scrollDown, this);
51968     up.addClassOnOver("x-scroller-btn-over");
51969     down.addClassOnOver("x-scroller-btn-over");
51970     up.addClassOnClick("x-scroller-btn-click");
51971     down.addClassOnClick("x-scroller-btn-click");
51972     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51973
51974     this.resizeEl = this.el;
51975     this.el = wrap; this.up = up; this.down = down;
51976 };
51977
51978 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51979     increment : 100,
51980     wheelIncrement : 5,
51981     scrollUp : function(){
51982         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51983     },
51984
51985     scrollDown : function(){
51986         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51987     },
51988
51989     afterScroll : function(){
51990         var el = this.resizeEl;
51991         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51992         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51993         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51994     },
51995
51996     setSize : function(){
51997         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51998         this.afterScroll();
51999     },
52000
52001     onWheel : function(e){
52002         var d = e.getWheelDelta();
52003         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52004         this.afterScroll();
52005         e.stopEvent();
52006     },
52007
52008     setContent : function(content, loadScripts){
52009         this.resizeEl.update(content, loadScripts);
52010     }
52011
52012 });
52013
52014
52015
52016
52017
52018
52019
52020
52021
52022 /**
52023  * @class Roo.TreePanel
52024  * @extends Roo.ContentPanel
52025  * @constructor
52026  * Create a new TreePanel. - defaults to fit/scoll contents.
52027  * @param {String/Object} config A string to set only the panel's title, or a config object
52028  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52029  */
52030 Roo.TreePanel = function(config){
52031     var el = config.el;
52032     var tree = config.tree;
52033     delete config.tree; 
52034     delete config.el; // hopefull!
52035     
52036     // wrapper for IE7 strict & safari scroll issue
52037     
52038     var treeEl = el.createChild();
52039     config.resizeEl = treeEl;
52040     
52041     
52042     
52043     Roo.TreePanel.superclass.constructor.call(this, el, config);
52044  
52045  
52046     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52047     //console.log(tree);
52048     this.on('activate', function()
52049     {
52050         if (this.tree.rendered) {
52051             return;
52052         }
52053         //console.log('render tree');
52054         this.tree.render();
52055     });
52056     // this should not be needed.. - it's actually the 'el' that resizes?
52057     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52058     
52059     //this.on('resize',  function (cp, w, h) {
52060     //        this.tree.innerCt.setWidth(w);
52061     //        this.tree.innerCt.setHeight(h);
52062     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52063     //});
52064
52065         
52066     
52067 };
52068
52069 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52070     fitToFrame : true,
52071     autoScroll : true
52072 });
52073
52074
52075
52076
52077
52078
52079
52080
52081
52082
52083
52084 /*
52085  * Based on:
52086  * Ext JS Library 1.1.1
52087  * Copyright(c) 2006-2007, Ext JS, LLC.
52088  *
52089  * Originally Released Under LGPL - original licence link has changed is not relivant.
52090  *
52091  * Fork - LGPL
52092  * <script type="text/javascript">
52093  */
52094  
52095
52096 /**
52097  * @class Roo.ReaderLayout
52098  * @extends Roo.BorderLayout
52099  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52100  * center region containing two nested regions (a top one for a list view and one for item preview below),
52101  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52102  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52103  * expedites the setup of the overall layout and regions for this common application style.
52104  * Example:
52105  <pre><code>
52106 var reader = new Roo.ReaderLayout();
52107 var CP = Roo.ContentPanel;  // shortcut for adding
52108
52109 reader.beginUpdate();
52110 reader.add("north", new CP("north", "North"));
52111 reader.add("west", new CP("west", {title: "West"}));
52112 reader.add("east", new CP("east", {title: "East"}));
52113
52114 reader.regions.listView.add(new CP("listView", "List"));
52115 reader.regions.preview.add(new CP("preview", "Preview"));
52116 reader.endUpdate();
52117 </code></pre>
52118 * @constructor
52119 * Create a new ReaderLayout
52120 * @param {Object} config Configuration options
52121 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52122 * document.body if omitted)
52123 */
52124 Roo.ReaderLayout = function(config, renderTo){
52125     var c = config || {size:{}};
52126     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52127         north: c.north !== false ? Roo.apply({
52128             split:false,
52129             initialSize: 32,
52130             titlebar: false
52131         }, c.north) : false,
52132         west: c.west !== false ? Roo.apply({
52133             split:true,
52134             initialSize: 200,
52135             minSize: 175,
52136             maxSize: 400,
52137             titlebar: true,
52138             collapsible: true,
52139             animate: true,
52140             margins:{left:5,right:0,bottom:5,top:5},
52141             cmargins:{left:5,right:5,bottom:5,top:5}
52142         }, c.west) : false,
52143         east: c.east !== false ? Roo.apply({
52144             split:true,
52145             initialSize: 200,
52146             minSize: 175,
52147             maxSize: 400,
52148             titlebar: true,
52149             collapsible: true,
52150             animate: true,
52151             margins:{left:0,right:5,bottom:5,top:5},
52152             cmargins:{left:5,right:5,bottom:5,top:5}
52153         }, c.east) : false,
52154         center: Roo.apply({
52155             tabPosition: 'top',
52156             autoScroll:false,
52157             closeOnTab: true,
52158             titlebar:false,
52159             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52160         }, c.center)
52161     });
52162
52163     this.el.addClass('x-reader');
52164
52165     this.beginUpdate();
52166
52167     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52168         south: c.preview !== false ? Roo.apply({
52169             split:true,
52170             initialSize: 200,
52171             minSize: 100,
52172             autoScroll:true,
52173             collapsible:true,
52174             titlebar: true,
52175             cmargins:{top:5,left:0, right:0, bottom:0}
52176         }, c.preview) : false,
52177         center: Roo.apply({
52178             autoScroll:false,
52179             titlebar:false,
52180             minHeight:200
52181         }, c.listView)
52182     });
52183     this.add('center', new Roo.NestedLayoutPanel(inner,
52184             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52185
52186     this.endUpdate();
52187
52188     this.regions.preview = inner.getRegion('south');
52189     this.regions.listView = inner.getRegion('center');
52190 };
52191
52192 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52193  * Based on:
52194  * Ext JS Library 1.1.1
52195  * Copyright(c) 2006-2007, Ext JS, LLC.
52196  *
52197  * Originally Released Under LGPL - original licence link has changed is not relivant.
52198  *
52199  * Fork - LGPL
52200  * <script type="text/javascript">
52201  */
52202  
52203 /**
52204  * @class Roo.grid.Grid
52205  * @extends Roo.util.Observable
52206  * This class represents the primary interface of a component based grid control.
52207  * <br><br>Usage:<pre><code>
52208  var grid = new Roo.grid.Grid("my-container-id", {
52209      ds: myDataStore,
52210      cm: myColModel,
52211      selModel: mySelectionModel,
52212      autoSizeColumns: true,
52213      monitorWindowResize: false,
52214      trackMouseOver: true
52215  });
52216  // set any options
52217  grid.render();
52218  * </code></pre>
52219  * <b>Common Problems:</b><br/>
52220  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52221  * element will correct this<br/>
52222  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52223  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52224  * are unpredictable.<br/>
52225  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52226  * grid to calculate dimensions/offsets.<br/>
52227   * @constructor
52228  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52229  * The container MUST have some type of size defined for the grid to fill. The container will be
52230  * automatically set to position relative if it isn't already.
52231  * @param {Object} config A config object that sets properties on this grid.
52232  */
52233 Roo.grid.Grid = function(container, config){
52234         // initialize the container
52235         this.container = Roo.get(container);
52236         this.container.update("");
52237         this.container.setStyle("overflow", "hidden");
52238     this.container.addClass('x-grid-container');
52239
52240     this.id = this.container.id;
52241
52242     Roo.apply(this, config);
52243     // check and correct shorthanded configs
52244     if(this.ds){
52245         this.dataSource = this.ds;
52246         delete this.ds;
52247     }
52248     if(this.cm){
52249         this.colModel = this.cm;
52250         delete this.cm;
52251     }
52252     if(this.sm){
52253         this.selModel = this.sm;
52254         delete this.sm;
52255     }
52256
52257     if (this.selModel) {
52258         this.selModel = Roo.factory(this.selModel, Roo.grid);
52259         this.sm = this.selModel;
52260         this.sm.xmodule = this.xmodule || false;
52261     }
52262     if (typeof(this.colModel.config) == 'undefined') {
52263         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52264         this.cm = this.colModel;
52265         this.cm.xmodule = this.xmodule || false;
52266     }
52267     if (this.dataSource) {
52268         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52269         this.ds = this.dataSource;
52270         this.ds.xmodule = this.xmodule || false;
52271          
52272     }
52273     
52274     
52275     
52276     if(this.width){
52277         this.container.setWidth(this.width);
52278     }
52279
52280     if(this.height){
52281         this.container.setHeight(this.height);
52282     }
52283     /** @private */
52284         this.addEvents({
52285         // raw events
52286         /**
52287          * @event click
52288          * The raw click event for the entire grid.
52289          * @param {Roo.EventObject} e
52290          */
52291         "click" : true,
52292         /**
52293          * @event dblclick
52294          * The raw dblclick event for the entire grid.
52295          * @param {Roo.EventObject} e
52296          */
52297         "dblclick" : true,
52298         /**
52299          * @event contextmenu
52300          * The raw contextmenu event for the entire grid.
52301          * @param {Roo.EventObject} e
52302          */
52303         "contextmenu" : true,
52304         /**
52305          * @event mousedown
52306          * The raw mousedown event for the entire grid.
52307          * @param {Roo.EventObject} e
52308          */
52309         "mousedown" : true,
52310         /**
52311          * @event mouseup
52312          * The raw mouseup event for the entire grid.
52313          * @param {Roo.EventObject} e
52314          */
52315         "mouseup" : true,
52316         /**
52317          * @event mouseover
52318          * The raw mouseover event for the entire grid.
52319          * @param {Roo.EventObject} e
52320          */
52321         "mouseover" : true,
52322         /**
52323          * @event mouseout
52324          * The raw mouseout event for the entire grid.
52325          * @param {Roo.EventObject} e
52326          */
52327         "mouseout" : true,
52328         /**
52329          * @event keypress
52330          * The raw keypress event for the entire grid.
52331          * @param {Roo.EventObject} e
52332          */
52333         "keypress" : true,
52334         /**
52335          * @event keydown
52336          * The raw keydown event for the entire grid.
52337          * @param {Roo.EventObject} e
52338          */
52339         "keydown" : true,
52340
52341         // custom events
52342
52343         /**
52344          * @event cellclick
52345          * Fires when a cell is clicked
52346          * @param {Grid} this
52347          * @param {Number} rowIndex
52348          * @param {Number} columnIndex
52349          * @param {Roo.EventObject} e
52350          */
52351         "cellclick" : true,
52352         /**
52353          * @event celldblclick
52354          * Fires when a cell is double clicked
52355          * @param {Grid} this
52356          * @param {Number} rowIndex
52357          * @param {Number} columnIndex
52358          * @param {Roo.EventObject} e
52359          */
52360         "celldblclick" : true,
52361         /**
52362          * @event rowclick
52363          * Fires when a row is clicked
52364          * @param {Grid} this
52365          * @param {Number} rowIndex
52366          * @param {Roo.EventObject} e
52367          */
52368         "rowclick" : true,
52369         /**
52370          * @event rowdblclick
52371          * Fires when a row is double clicked
52372          * @param {Grid} this
52373          * @param {Number} rowIndex
52374          * @param {Roo.EventObject} e
52375          */
52376         "rowdblclick" : true,
52377         /**
52378          * @event headerclick
52379          * Fires when a header is clicked
52380          * @param {Grid} this
52381          * @param {Number} columnIndex
52382          * @param {Roo.EventObject} e
52383          */
52384         "headerclick" : true,
52385         /**
52386          * @event headerdblclick
52387          * Fires when a header cell is double clicked
52388          * @param {Grid} this
52389          * @param {Number} columnIndex
52390          * @param {Roo.EventObject} e
52391          */
52392         "headerdblclick" : true,
52393         /**
52394          * @event rowcontextmenu
52395          * Fires when a row is right clicked
52396          * @param {Grid} this
52397          * @param {Number} rowIndex
52398          * @param {Roo.EventObject} e
52399          */
52400         "rowcontextmenu" : true,
52401         /**
52402          * @event cellcontextmenu
52403          * Fires when a cell is right clicked
52404          * @param {Grid} this
52405          * @param {Number} rowIndex
52406          * @param {Number} cellIndex
52407          * @param {Roo.EventObject} e
52408          */
52409          "cellcontextmenu" : true,
52410         /**
52411          * @event headercontextmenu
52412          * Fires when a header is right clicked
52413          * @param {Grid} this
52414          * @param {Number} columnIndex
52415          * @param {Roo.EventObject} e
52416          */
52417         "headercontextmenu" : true,
52418         /**
52419          * @event bodyscroll
52420          * Fires when the body element is scrolled
52421          * @param {Number} scrollLeft
52422          * @param {Number} scrollTop
52423          */
52424         "bodyscroll" : true,
52425         /**
52426          * @event columnresize
52427          * Fires when the user resizes a column
52428          * @param {Number} columnIndex
52429          * @param {Number} newSize
52430          */
52431         "columnresize" : true,
52432         /**
52433          * @event columnmove
52434          * Fires when the user moves a column
52435          * @param {Number} oldIndex
52436          * @param {Number} newIndex
52437          */
52438         "columnmove" : true,
52439         /**
52440          * @event startdrag
52441          * Fires when row(s) start being dragged
52442          * @param {Grid} this
52443          * @param {Roo.GridDD} dd The drag drop object
52444          * @param {event} e The raw browser event
52445          */
52446         "startdrag" : true,
52447         /**
52448          * @event enddrag
52449          * Fires when a drag operation is complete
52450          * @param {Grid} this
52451          * @param {Roo.GridDD} dd The drag drop object
52452          * @param {event} e The raw browser event
52453          */
52454         "enddrag" : true,
52455         /**
52456          * @event dragdrop
52457          * Fires when dragged row(s) are dropped on a valid DD target
52458          * @param {Grid} this
52459          * @param {Roo.GridDD} dd The drag drop object
52460          * @param {String} targetId The target drag drop object
52461          * @param {event} e The raw browser event
52462          */
52463         "dragdrop" : true,
52464         /**
52465          * @event dragover
52466          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52467          * @param {Grid} this
52468          * @param {Roo.GridDD} dd The drag drop object
52469          * @param {String} targetId The target drag drop object
52470          * @param {event} e The raw browser event
52471          */
52472         "dragover" : true,
52473         /**
52474          * @event dragenter
52475          *  Fires when the dragged row(s) first cross another DD target while being dragged
52476          * @param {Grid} this
52477          * @param {Roo.GridDD} dd The drag drop object
52478          * @param {String} targetId The target drag drop object
52479          * @param {event} e The raw browser event
52480          */
52481         "dragenter" : true,
52482         /**
52483          * @event dragout
52484          * Fires when the dragged row(s) leave another DD target while being dragged
52485          * @param {Grid} this
52486          * @param {Roo.GridDD} dd The drag drop object
52487          * @param {String} targetId The target drag drop object
52488          * @param {event} e The raw browser event
52489          */
52490         "dragout" : true,
52491         /**
52492          * @event rowclass
52493          * Fires when a row is rendered, so you can change add a style to it.
52494          * @param {GridView} gridview   The grid view
52495          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52496          */
52497         'rowclass' : true,
52498
52499         /**
52500          * @event render
52501          * Fires when the grid is rendered
52502          * @param {Grid} grid
52503          */
52504         'render' : true
52505     });
52506
52507     Roo.grid.Grid.superclass.constructor.call(this);
52508 };
52509 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52510     
52511     /**
52512      * @cfg {String} ddGroup - drag drop group.
52513      */
52514
52515     /**
52516      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52517      */
52518     minColumnWidth : 25,
52519
52520     /**
52521      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52522      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52523      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52524      */
52525     autoSizeColumns : false,
52526
52527     /**
52528      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52529      */
52530     autoSizeHeaders : true,
52531
52532     /**
52533      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52534      */
52535     monitorWindowResize : true,
52536
52537     /**
52538      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52539      * rows measured to get a columns size. Default is 0 (all rows).
52540      */
52541     maxRowsToMeasure : 0,
52542
52543     /**
52544      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52545      */
52546     trackMouseOver : true,
52547
52548     /**
52549     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52550     */
52551     
52552     /**
52553     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52554     */
52555     enableDragDrop : false,
52556     
52557     /**
52558     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52559     */
52560     enableColumnMove : true,
52561     
52562     /**
52563     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52564     */
52565     enableColumnHide : true,
52566     
52567     /**
52568     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52569     */
52570     enableRowHeightSync : false,
52571     
52572     /**
52573     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52574     */
52575     stripeRows : true,
52576     
52577     /**
52578     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52579     */
52580     autoHeight : false,
52581
52582     /**
52583      * @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.
52584      */
52585     autoExpandColumn : false,
52586
52587     /**
52588     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52589     * Default is 50.
52590     */
52591     autoExpandMin : 50,
52592
52593     /**
52594     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52595     */
52596     autoExpandMax : 1000,
52597
52598     /**
52599     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52600     */
52601     view : null,
52602
52603     /**
52604     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52605     */
52606     loadMask : false,
52607     /**
52608     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52609     */
52610     dropTarget: false,
52611     
52612    
52613     
52614     // private
52615     rendered : false,
52616
52617     /**
52618     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52619     * of a fixed width. Default is false.
52620     */
52621     /**
52622     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52623     */
52624     /**
52625      * Called once after all setup has been completed and the grid is ready to be rendered.
52626      * @return {Roo.grid.Grid} this
52627      */
52628     render : function()
52629     {
52630         var c = this.container;
52631         // try to detect autoHeight/width mode
52632         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52633             this.autoHeight = true;
52634         }
52635         var view = this.getView();
52636         view.init(this);
52637
52638         c.on("click", this.onClick, this);
52639         c.on("dblclick", this.onDblClick, this);
52640         c.on("contextmenu", this.onContextMenu, this);
52641         c.on("keydown", this.onKeyDown, this);
52642         if (Roo.isTouch) {
52643             c.on("touchstart", this.onTouchStart, this);
52644         }
52645
52646         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52647
52648         this.getSelectionModel().init(this);
52649
52650         view.render();
52651
52652         if(this.loadMask){
52653             this.loadMask = new Roo.LoadMask(this.container,
52654                     Roo.apply({store:this.dataSource}, this.loadMask));
52655         }
52656         
52657         
52658         if (this.toolbar && this.toolbar.xtype) {
52659             this.toolbar.container = this.getView().getHeaderPanel(true);
52660             this.toolbar = new Roo.Toolbar(this.toolbar);
52661         }
52662         if (this.footer && this.footer.xtype) {
52663             this.footer.dataSource = this.getDataSource();
52664             this.footer.container = this.getView().getFooterPanel(true);
52665             this.footer = Roo.factory(this.footer, Roo);
52666         }
52667         if (this.dropTarget && this.dropTarget.xtype) {
52668             delete this.dropTarget.xtype;
52669             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52670         }
52671         
52672         
52673         this.rendered = true;
52674         this.fireEvent('render', this);
52675         return this;
52676     },
52677
52678         /**
52679          * Reconfigures the grid to use a different Store and Column Model.
52680          * The View will be bound to the new objects and refreshed.
52681          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52682          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52683          */
52684     reconfigure : function(dataSource, colModel){
52685         if(this.loadMask){
52686             this.loadMask.destroy();
52687             this.loadMask = new Roo.LoadMask(this.container,
52688                     Roo.apply({store:dataSource}, this.loadMask));
52689         }
52690         this.view.bind(dataSource, colModel);
52691         this.dataSource = dataSource;
52692         this.colModel = colModel;
52693         this.view.refresh(true);
52694     },
52695
52696     // private
52697     onKeyDown : function(e){
52698         this.fireEvent("keydown", e);
52699     },
52700
52701     /**
52702      * Destroy this grid.
52703      * @param {Boolean} removeEl True to remove the element
52704      */
52705     destroy : function(removeEl, keepListeners){
52706         if(this.loadMask){
52707             this.loadMask.destroy();
52708         }
52709         var c = this.container;
52710         c.removeAllListeners();
52711         this.view.destroy();
52712         this.colModel.purgeListeners();
52713         if(!keepListeners){
52714             this.purgeListeners();
52715         }
52716         c.update("");
52717         if(removeEl === true){
52718             c.remove();
52719         }
52720     },
52721
52722     // private
52723     processEvent : function(name, e){
52724         // does this fire select???
52725         //Roo.log('grid:processEvent '  + name);
52726         
52727         if (name != 'touchstart' ) {
52728             this.fireEvent(name, e);    
52729         }
52730         
52731         var t = e.getTarget();
52732         var v = this.view;
52733         var header = v.findHeaderIndex(t);
52734         if(header !== false){
52735             var ename = name == 'touchstart' ? 'click' : name;
52736              
52737             this.fireEvent("header" + ename, this, header, e);
52738         }else{
52739             var row = v.findRowIndex(t);
52740             var cell = v.findCellIndex(t);
52741             if (name == 'touchstart') {
52742                 // first touch is always a click.
52743                 // hopefull this happens after selection is updated.?
52744                 name = false;
52745                 
52746                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52747                     var cs = this.selModel.getSelectedCell();
52748                     if (row == cs[0] && cell == cs[1]){
52749                         name = 'dblclick';
52750                     }
52751                 }
52752                 if (typeof(this.selModel.getSelections) != 'undefined') {
52753                     var cs = this.selModel.getSelections();
52754                     var ds = this.dataSource;
52755                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52756                         name = 'dblclick';
52757                     }
52758                 }
52759                 if (!name) {
52760                     return;
52761                 }
52762             }
52763             
52764             
52765             if(row !== false){
52766                 this.fireEvent("row" + name, this, row, e);
52767                 if(cell !== false){
52768                     this.fireEvent("cell" + name, this, row, cell, e);
52769                 }
52770             }
52771         }
52772     },
52773
52774     // private
52775     onClick : function(e){
52776         this.processEvent("click", e);
52777     },
52778    // private
52779     onTouchStart : function(e){
52780         this.processEvent("touchstart", e);
52781     },
52782
52783     // private
52784     onContextMenu : function(e, t){
52785         this.processEvent("contextmenu", e);
52786     },
52787
52788     // private
52789     onDblClick : function(e){
52790         this.processEvent("dblclick", e);
52791     },
52792
52793     // private
52794     walkCells : function(row, col, step, fn, scope){
52795         var cm = this.colModel, clen = cm.getColumnCount();
52796         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52797         if(step < 0){
52798             if(col < 0){
52799                 row--;
52800                 first = false;
52801             }
52802             while(row >= 0){
52803                 if(!first){
52804                     col = clen-1;
52805                 }
52806                 first = false;
52807                 while(col >= 0){
52808                     if(fn.call(scope || this, row, col, cm) === true){
52809                         return [row, col];
52810                     }
52811                     col--;
52812                 }
52813                 row--;
52814             }
52815         } else {
52816             if(col >= clen){
52817                 row++;
52818                 first = false;
52819             }
52820             while(row < rlen){
52821                 if(!first){
52822                     col = 0;
52823                 }
52824                 first = false;
52825                 while(col < clen){
52826                     if(fn.call(scope || this, row, col, cm) === true){
52827                         return [row, col];
52828                     }
52829                     col++;
52830                 }
52831                 row++;
52832             }
52833         }
52834         return null;
52835     },
52836
52837     // private
52838     getSelections : function(){
52839         return this.selModel.getSelections();
52840     },
52841
52842     /**
52843      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52844      * but if manual update is required this method will initiate it.
52845      */
52846     autoSize : function(){
52847         if(this.rendered){
52848             this.view.layout();
52849             if(this.view.adjustForScroll){
52850                 this.view.adjustForScroll();
52851             }
52852         }
52853     },
52854
52855     /**
52856      * Returns the grid's underlying element.
52857      * @return {Element} The element
52858      */
52859     getGridEl : function(){
52860         return this.container;
52861     },
52862
52863     // private for compatibility, overridden by editor grid
52864     stopEditing : function(){},
52865
52866     /**
52867      * Returns the grid's SelectionModel.
52868      * @return {SelectionModel}
52869      */
52870     getSelectionModel : function(){
52871         if(!this.selModel){
52872             this.selModel = new Roo.grid.RowSelectionModel();
52873         }
52874         return this.selModel;
52875     },
52876
52877     /**
52878      * Returns the grid's DataSource.
52879      * @return {DataSource}
52880      */
52881     getDataSource : function(){
52882         return this.dataSource;
52883     },
52884
52885     /**
52886      * Returns the grid's ColumnModel.
52887      * @return {ColumnModel}
52888      */
52889     getColumnModel : function(){
52890         return this.colModel;
52891     },
52892
52893     /**
52894      * Returns the grid's GridView object.
52895      * @return {GridView}
52896      */
52897     getView : function(){
52898         if(!this.view){
52899             this.view = new Roo.grid.GridView(this.viewConfig);
52900         }
52901         return this.view;
52902     },
52903     /**
52904      * Called to get grid's drag proxy text, by default returns this.ddText.
52905      * @return {String}
52906      */
52907     getDragDropText : function(){
52908         var count = this.selModel.getCount();
52909         return String.format(this.ddText, count, count == 1 ? '' : 's');
52910     }
52911 });
52912 /**
52913  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52914  * %0 is replaced with the number of selected rows.
52915  * @type String
52916  */
52917 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52918  * Based on:
52919  * Ext JS Library 1.1.1
52920  * Copyright(c) 2006-2007, Ext JS, LLC.
52921  *
52922  * Originally Released Under LGPL - original licence link has changed is not relivant.
52923  *
52924  * Fork - LGPL
52925  * <script type="text/javascript">
52926  */
52927  
52928 Roo.grid.AbstractGridView = function(){
52929         this.grid = null;
52930         
52931         this.events = {
52932             "beforerowremoved" : true,
52933             "beforerowsinserted" : true,
52934             "beforerefresh" : true,
52935             "rowremoved" : true,
52936             "rowsinserted" : true,
52937             "rowupdated" : true,
52938             "refresh" : true
52939         };
52940     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52941 };
52942
52943 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52944     rowClass : "x-grid-row",
52945     cellClass : "x-grid-cell",
52946     tdClass : "x-grid-td",
52947     hdClass : "x-grid-hd",
52948     splitClass : "x-grid-hd-split",
52949     
52950     init: function(grid){
52951         this.grid = grid;
52952                 var cid = this.grid.getGridEl().id;
52953         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52954         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52955         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52956         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52957         },
52958         
52959     getColumnRenderers : function(){
52960         var renderers = [];
52961         var cm = this.grid.colModel;
52962         var colCount = cm.getColumnCount();
52963         for(var i = 0; i < colCount; i++){
52964             renderers[i] = cm.getRenderer(i);
52965         }
52966         return renderers;
52967     },
52968     
52969     getColumnIds : function(){
52970         var ids = [];
52971         var cm = this.grid.colModel;
52972         var colCount = cm.getColumnCount();
52973         for(var i = 0; i < colCount; i++){
52974             ids[i] = cm.getColumnId(i);
52975         }
52976         return ids;
52977     },
52978     
52979     getDataIndexes : function(){
52980         if(!this.indexMap){
52981             this.indexMap = this.buildIndexMap();
52982         }
52983         return this.indexMap.colToData;
52984     },
52985     
52986     getColumnIndexByDataIndex : function(dataIndex){
52987         if(!this.indexMap){
52988             this.indexMap = this.buildIndexMap();
52989         }
52990         return this.indexMap.dataToCol[dataIndex];
52991     },
52992     
52993     /**
52994      * Set a css style for a column dynamically. 
52995      * @param {Number} colIndex The index of the column
52996      * @param {String} name The css property name
52997      * @param {String} value The css value
52998      */
52999     setCSSStyle : function(colIndex, name, value){
53000         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53001         Roo.util.CSS.updateRule(selector, name, value);
53002     },
53003     
53004     generateRules : function(cm){
53005         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53006         Roo.util.CSS.removeStyleSheet(rulesId);
53007         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53008             var cid = cm.getColumnId(i);
53009             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53010                          this.tdSelector, cid, " {\n}\n",
53011                          this.hdSelector, cid, " {\n}\n",
53012                          this.splitSelector, cid, " {\n}\n");
53013         }
53014         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53015     }
53016 });/*
53017  * Based on:
53018  * Ext JS Library 1.1.1
53019  * Copyright(c) 2006-2007, Ext JS, LLC.
53020  *
53021  * Originally Released Under LGPL - original licence link has changed is not relivant.
53022  *
53023  * Fork - LGPL
53024  * <script type="text/javascript">
53025  */
53026
53027 // private
53028 // This is a support class used internally by the Grid components
53029 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53030     this.grid = grid;
53031     this.view = grid.getView();
53032     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53033     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53034     if(hd2){
53035         this.setHandleElId(Roo.id(hd));
53036         this.setOuterHandleElId(Roo.id(hd2));
53037     }
53038     this.scroll = false;
53039 };
53040 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53041     maxDragWidth: 120,
53042     getDragData : function(e){
53043         var t = Roo.lib.Event.getTarget(e);
53044         var h = this.view.findHeaderCell(t);
53045         if(h){
53046             return {ddel: h.firstChild, header:h};
53047         }
53048         return false;
53049     },
53050
53051     onInitDrag : function(e){
53052         this.view.headersDisabled = true;
53053         var clone = this.dragData.ddel.cloneNode(true);
53054         clone.id = Roo.id();
53055         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53056         this.proxy.update(clone);
53057         return true;
53058     },
53059
53060     afterValidDrop : function(){
53061         var v = this.view;
53062         setTimeout(function(){
53063             v.headersDisabled = false;
53064         }, 50);
53065     },
53066
53067     afterInvalidDrop : function(){
53068         var v = this.view;
53069         setTimeout(function(){
53070             v.headersDisabled = false;
53071         }, 50);
53072     }
53073 });
53074 /*
53075  * Based on:
53076  * Ext JS Library 1.1.1
53077  * Copyright(c) 2006-2007, Ext JS, LLC.
53078  *
53079  * Originally Released Under LGPL - original licence link has changed is not relivant.
53080  *
53081  * Fork - LGPL
53082  * <script type="text/javascript">
53083  */
53084 // private
53085 // This is a support class used internally by the Grid components
53086 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53087     this.grid = grid;
53088     this.view = grid.getView();
53089     // split the proxies so they don't interfere with mouse events
53090     this.proxyTop = Roo.DomHelper.append(document.body, {
53091         cls:"col-move-top", html:"&#160;"
53092     }, true);
53093     this.proxyBottom = Roo.DomHelper.append(document.body, {
53094         cls:"col-move-bottom", html:"&#160;"
53095     }, true);
53096     this.proxyTop.hide = this.proxyBottom.hide = function(){
53097         this.setLeftTop(-100,-100);
53098         this.setStyle("visibility", "hidden");
53099     };
53100     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53101     // temporarily disabled
53102     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53103     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53104 };
53105 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53106     proxyOffsets : [-4, -9],
53107     fly: Roo.Element.fly,
53108
53109     getTargetFromEvent : function(e){
53110         var t = Roo.lib.Event.getTarget(e);
53111         var cindex = this.view.findCellIndex(t);
53112         if(cindex !== false){
53113             return this.view.getHeaderCell(cindex);
53114         }
53115         return null;
53116     },
53117
53118     nextVisible : function(h){
53119         var v = this.view, cm = this.grid.colModel;
53120         h = h.nextSibling;
53121         while(h){
53122             if(!cm.isHidden(v.getCellIndex(h))){
53123                 return h;
53124             }
53125             h = h.nextSibling;
53126         }
53127         return null;
53128     },
53129
53130     prevVisible : function(h){
53131         var v = this.view, cm = this.grid.colModel;
53132         h = h.prevSibling;
53133         while(h){
53134             if(!cm.isHidden(v.getCellIndex(h))){
53135                 return h;
53136             }
53137             h = h.prevSibling;
53138         }
53139         return null;
53140     },
53141
53142     positionIndicator : function(h, n, e){
53143         var x = Roo.lib.Event.getPageX(e);
53144         var r = Roo.lib.Dom.getRegion(n.firstChild);
53145         var px, pt, py = r.top + this.proxyOffsets[1];
53146         if((r.right - x) <= (r.right-r.left)/2){
53147             px = r.right+this.view.borderWidth;
53148             pt = "after";
53149         }else{
53150             px = r.left;
53151             pt = "before";
53152         }
53153         var oldIndex = this.view.getCellIndex(h);
53154         var newIndex = this.view.getCellIndex(n);
53155
53156         if(this.grid.colModel.isFixed(newIndex)){
53157             return false;
53158         }
53159
53160         var locked = this.grid.colModel.isLocked(newIndex);
53161
53162         if(pt == "after"){
53163             newIndex++;
53164         }
53165         if(oldIndex < newIndex){
53166             newIndex--;
53167         }
53168         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53169             return false;
53170         }
53171         px +=  this.proxyOffsets[0];
53172         this.proxyTop.setLeftTop(px, py);
53173         this.proxyTop.show();
53174         if(!this.bottomOffset){
53175             this.bottomOffset = this.view.mainHd.getHeight();
53176         }
53177         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53178         this.proxyBottom.show();
53179         return pt;
53180     },
53181
53182     onNodeEnter : function(n, dd, e, data){
53183         if(data.header != n){
53184             this.positionIndicator(data.header, n, e);
53185         }
53186     },
53187
53188     onNodeOver : function(n, dd, e, data){
53189         var result = false;
53190         if(data.header != n){
53191             result = this.positionIndicator(data.header, n, e);
53192         }
53193         if(!result){
53194             this.proxyTop.hide();
53195             this.proxyBottom.hide();
53196         }
53197         return result ? this.dropAllowed : this.dropNotAllowed;
53198     },
53199
53200     onNodeOut : function(n, dd, e, data){
53201         this.proxyTop.hide();
53202         this.proxyBottom.hide();
53203     },
53204
53205     onNodeDrop : function(n, dd, e, data){
53206         var h = data.header;
53207         if(h != n){
53208             var cm = this.grid.colModel;
53209             var x = Roo.lib.Event.getPageX(e);
53210             var r = Roo.lib.Dom.getRegion(n.firstChild);
53211             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53212             var oldIndex = this.view.getCellIndex(h);
53213             var newIndex = this.view.getCellIndex(n);
53214             var locked = cm.isLocked(newIndex);
53215             if(pt == "after"){
53216                 newIndex++;
53217             }
53218             if(oldIndex < newIndex){
53219                 newIndex--;
53220             }
53221             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53222                 return false;
53223             }
53224             cm.setLocked(oldIndex, locked, true);
53225             cm.moveColumn(oldIndex, newIndex);
53226             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53227             return true;
53228         }
53229         return false;
53230     }
53231 });
53232 /*
53233  * Based on:
53234  * Ext JS Library 1.1.1
53235  * Copyright(c) 2006-2007, Ext JS, LLC.
53236  *
53237  * Originally Released Under LGPL - original licence link has changed is not relivant.
53238  *
53239  * Fork - LGPL
53240  * <script type="text/javascript">
53241  */
53242   
53243 /**
53244  * @class Roo.grid.GridView
53245  * @extends Roo.util.Observable
53246  *
53247  * @constructor
53248  * @param {Object} config
53249  */
53250 Roo.grid.GridView = function(config){
53251     Roo.grid.GridView.superclass.constructor.call(this);
53252     this.el = null;
53253
53254     Roo.apply(this, config);
53255 };
53256
53257 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53258
53259     unselectable :  'unselectable="on"',
53260     unselectableCls :  'x-unselectable',
53261     
53262     
53263     rowClass : "x-grid-row",
53264
53265     cellClass : "x-grid-col",
53266
53267     tdClass : "x-grid-td",
53268
53269     hdClass : "x-grid-hd",
53270
53271     splitClass : "x-grid-split",
53272
53273     sortClasses : ["sort-asc", "sort-desc"],
53274
53275     enableMoveAnim : false,
53276
53277     hlColor: "C3DAF9",
53278
53279     dh : Roo.DomHelper,
53280
53281     fly : Roo.Element.fly,
53282
53283     css : Roo.util.CSS,
53284
53285     borderWidth: 1,
53286
53287     splitOffset: 3,
53288
53289     scrollIncrement : 22,
53290
53291     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53292
53293     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53294
53295     bind : function(ds, cm){
53296         if(this.ds){
53297             this.ds.un("load", this.onLoad, this);
53298             this.ds.un("datachanged", this.onDataChange, this);
53299             this.ds.un("add", this.onAdd, this);
53300             this.ds.un("remove", this.onRemove, this);
53301             this.ds.un("update", this.onUpdate, this);
53302             this.ds.un("clear", this.onClear, this);
53303         }
53304         if(ds){
53305             ds.on("load", this.onLoad, this);
53306             ds.on("datachanged", this.onDataChange, this);
53307             ds.on("add", this.onAdd, this);
53308             ds.on("remove", this.onRemove, this);
53309             ds.on("update", this.onUpdate, this);
53310             ds.on("clear", this.onClear, this);
53311         }
53312         this.ds = ds;
53313
53314         if(this.cm){
53315             this.cm.un("widthchange", this.onColWidthChange, this);
53316             this.cm.un("headerchange", this.onHeaderChange, this);
53317             this.cm.un("hiddenchange", this.onHiddenChange, this);
53318             this.cm.un("columnmoved", this.onColumnMove, this);
53319             this.cm.un("columnlockchange", this.onColumnLock, this);
53320         }
53321         if(cm){
53322             this.generateRules(cm);
53323             cm.on("widthchange", this.onColWidthChange, this);
53324             cm.on("headerchange", this.onHeaderChange, this);
53325             cm.on("hiddenchange", this.onHiddenChange, this);
53326             cm.on("columnmoved", this.onColumnMove, this);
53327             cm.on("columnlockchange", this.onColumnLock, this);
53328         }
53329         this.cm = cm;
53330     },
53331
53332     init: function(grid){
53333         Roo.grid.GridView.superclass.init.call(this, grid);
53334
53335         this.bind(grid.dataSource, grid.colModel);
53336
53337         grid.on("headerclick", this.handleHeaderClick, this);
53338
53339         if(grid.trackMouseOver){
53340             grid.on("mouseover", this.onRowOver, this);
53341             grid.on("mouseout", this.onRowOut, this);
53342         }
53343         grid.cancelTextSelection = function(){};
53344         this.gridId = grid.id;
53345
53346         var tpls = this.templates || {};
53347
53348         if(!tpls.master){
53349             tpls.master = new Roo.Template(
53350                '<div class="x-grid" hidefocus="true">',
53351                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53352                   '<div class="x-grid-topbar"></div>',
53353                   '<div class="x-grid-scroller"><div></div></div>',
53354                   '<div class="x-grid-locked">',
53355                       '<div class="x-grid-header">{lockedHeader}</div>',
53356                       '<div class="x-grid-body">{lockedBody}</div>',
53357                   "</div>",
53358                   '<div class="x-grid-viewport">',
53359                       '<div class="x-grid-header">{header}</div>',
53360                       '<div class="x-grid-body">{body}</div>',
53361                   "</div>",
53362                   '<div class="x-grid-bottombar"></div>',
53363                  
53364                   '<div class="x-grid-resize-proxy">&#160;</div>',
53365                "</div>"
53366             );
53367             tpls.master.disableformats = true;
53368         }
53369
53370         if(!tpls.header){
53371             tpls.header = new Roo.Template(
53372                '<table border="0" cellspacing="0" cellpadding="0">',
53373                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53374                "</table>{splits}"
53375             );
53376             tpls.header.disableformats = true;
53377         }
53378         tpls.header.compile();
53379
53380         if(!tpls.hcell){
53381             tpls.hcell = new Roo.Template(
53382                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53383                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53384                 "</div></td>"
53385              );
53386              tpls.hcell.disableFormats = true;
53387         }
53388         tpls.hcell.compile();
53389
53390         if(!tpls.hsplit){
53391             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53392                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53393             tpls.hsplit.disableFormats = true;
53394         }
53395         tpls.hsplit.compile();
53396
53397         if(!tpls.body){
53398             tpls.body = new Roo.Template(
53399                '<table border="0" cellspacing="0" cellpadding="0">',
53400                "<tbody>{rows}</tbody>",
53401                "</table>"
53402             );
53403             tpls.body.disableFormats = true;
53404         }
53405         tpls.body.compile();
53406
53407         if(!tpls.row){
53408             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53409             tpls.row.disableFormats = true;
53410         }
53411         tpls.row.compile();
53412
53413         if(!tpls.cell){
53414             tpls.cell = new Roo.Template(
53415                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53416                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53417                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53418                 "</td>"
53419             );
53420             tpls.cell.disableFormats = true;
53421         }
53422         tpls.cell.compile();
53423
53424         this.templates = tpls;
53425     },
53426
53427     // remap these for backwards compat
53428     onColWidthChange : function(){
53429         this.updateColumns.apply(this, arguments);
53430     },
53431     onHeaderChange : function(){
53432         this.updateHeaders.apply(this, arguments);
53433     }, 
53434     onHiddenChange : function(){
53435         this.handleHiddenChange.apply(this, arguments);
53436     },
53437     onColumnMove : function(){
53438         this.handleColumnMove.apply(this, arguments);
53439     },
53440     onColumnLock : function(){
53441         this.handleLockChange.apply(this, arguments);
53442     },
53443
53444     onDataChange : function(){
53445         this.refresh();
53446         this.updateHeaderSortState();
53447     },
53448
53449     onClear : function(){
53450         this.refresh();
53451     },
53452
53453     onUpdate : function(ds, record){
53454         this.refreshRow(record);
53455     },
53456
53457     refreshRow : function(record){
53458         var ds = this.ds, index;
53459         if(typeof record == 'number'){
53460             index = record;
53461             record = ds.getAt(index);
53462         }else{
53463             index = ds.indexOf(record);
53464         }
53465         this.insertRows(ds, index, index, true);
53466         this.onRemove(ds, record, index+1, true);
53467         this.syncRowHeights(index, index);
53468         this.layout();
53469         this.fireEvent("rowupdated", this, index, record);
53470     },
53471
53472     onAdd : function(ds, records, index){
53473         this.insertRows(ds, index, index + (records.length-1));
53474     },
53475
53476     onRemove : function(ds, record, index, isUpdate){
53477         if(isUpdate !== true){
53478             this.fireEvent("beforerowremoved", this, index, record);
53479         }
53480         var bt = this.getBodyTable(), lt = this.getLockedTable();
53481         if(bt.rows[index]){
53482             bt.firstChild.removeChild(bt.rows[index]);
53483         }
53484         if(lt.rows[index]){
53485             lt.firstChild.removeChild(lt.rows[index]);
53486         }
53487         if(isUpdate !== true){
53488             this.stripeRows(index);
53489             this.syncRowHeights(index, index);
53490             this.layout();
53491             this.fireEvent("rowremoved", this, index, record);
53492         }
53493     },
53494
53495     onLoad : function(){
53496         this.scrollToTop();
53497     },
53498
53499     /**
53500      * Scrolls the grid to the top
53501      */
53502     scrollToTop : function(){
53503         if(this.scroller){
53504             this.scroller.dom.scrollTop = 0;
53505             this.syncScroll();
53506         }
53507     },
53508
53509     /**
53510      * Gets a panel in the header of the grid that can be used for toolbars etc.
53511      * After modifying the contents of this panel a call to grid.autoSize() may be
53512      * required to register any changes in size.
53513      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53514      * @return Roo.Element
53515      */
53516     getHeaderPanel : function(doShow){
53517         if(doShow){
53518             this.headerPanel.show();
53519         }
53520         return this.headerPanel;
53521     },
53522
53523     /**
53524      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53525      * After modifying the contents of this panel a call to grid.autoSize() may be
53526      * required to register any changes in size.
53527      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53528      * @return Roo.Element
53529      */
53530     getFooterPanel : function(doShow){
53531         if(doShow){
53532             this.footerPanel.show();
53533         }
53534         return this.footerPanel;
53535     },
53536
53537     initElements : function(){
53538         var E = Roo.Element;
53539         var el = this.grid.getGridEl().dom.firstChild;
53540         var cs = el.childNodes;
53541
53542         this.el = new E(el);
53543         
53544          this.focusEl = new E(el.firstChild);
53545         this.focusEl.swallowEvent("click", true);
53546         
53547         this.headerPanel = new E(cs[1]);
53548         this.headerPanel.enableDisplayMode("block");
53549
53550         this.scroller = new E(cs[2]);
53551         this.scrollSizer = new E(this.scroller.dom.firstChild);
53552
53553         this.lockedWrap = new E(cs[3]);
53554         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53555         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53556
53557         this.mainWrap = new E(cs[4]);
53558         this.mainHd = new E(this.mainWrap.dom.firstChild);
53559         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53560
53561         this.footerPanel = new E(cs[5]);
53562         this.footerPanel.enableDisplayMode("block");
53563
53564         this.resizeProxy = new E(cs[6]);
53565
53566         this.headerSelector = String.format(
53567            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53568            this.lockedHd.id, this.mainHd.id
53569         );
53570
53571         this.splitterSelector = String.format(
53572            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53573            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53574         );
53575     },
53576     idToCssName : function(s)
53577     {
53578         return s.replace(/[^a-z0-9]+/ig, '-');
53579     },
53580
53581     getHeaderCell : function(index){
53582         return Roo.DomQuery.select(this.headerSelector)[index];
53583     },
53584
53585     getHeaderCellMeasure : function(index){
53586         return this.getHeaderCell(index).firstChild;
53587     },
53588
53589     getHeaderCellText : function(index){
53590         return this.getHeaderCell(index).firstChild.firstChild;
53591     },
53592
53593     getLockedTable : function(){
53594         return this.lockedBody.dom.firstChild;
53595     },
53596
53597     getBodyTable : function(){
53598         return this.mainBody.dom.firstChild;
53599     },
53600
53601     getLockedRow : function(index){
53602         return this.getLockedTable().rows[index];
53603     },
53604
53605     getRow : function(index){
53606         return this.getBodyTable().rows[index];
53607     },
53608
53609     getRowComposite : function(index){
53610         if(!this.rowEl){
53611             this.rowEl = new Roo.CompositeElementLite();
53612         }
53613         var els = [], lrow, mrow;
53614         if(lrow = this.getLockedRow(index)){
53615             els.push(lrow);
53616         }
53617         if(mrow = this.getRow(index)){
53618             els.push(mrow);
53619         }
53620         this.rowEl.elements = els;
53621         return this.rowEl;
53622     },
53623     /**
53624      * Gets the 'td' of the cell
53625      * 
53626      * @param {Integer} rowIndex row to select
53627      * @param {Integer} colIndex column to select
53628      * 
53629      * @return {Object} 
53630      */
53631     getCell : function(rowIndex, colIndex){
53632         var locked = this.cm.getLockedCount();
53633         var source;
53634         if(colIndex < locked){
53635             source = this.lockedBody.dom.firstChild;
53636         }else{
53637             source = this.mainBody.dom.firstChild;
53638             colIndex -= locked;
53639         }
53640         return source.rows[rowIndex].childNodes[colIndex];
53641     },
53642
53643     getCellText : function(rowIndex, colIndex){
53644         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53645     },
53646
53647     getCellBox : function(cell){
53648         var b = this.fly(cell).getBox();
53649         if(Roo.isOpera){ // opera fails to report the Y
53650             b.y = cell.offsetTop + this.mainBody.getY();
53651         }
53652         return b;
53653     },
53654
53655     getCellIndex : function(cell){
53656         var id = String(cell.className).match(this.cellRE);
53657         if(id){
53658             return parseInt(id[1], 10);
53659         }
53660         return 0;
53661     },
53662
53663     findHeaderIndex : function(n){
53664         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53665         return r ? this.getCellIndex(r) : false;
53666     },
53667
53668     findHeaderCell : function(n){
53669         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53670         return r ? r : false;
53671     },
53672
53673     findRowIndex : function(n){
53674         if(!n){
53675             return false;
53676         }
53677         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53678         return r ? r.rowIndex : false;
53679     },
53680
53681     findCellIndex : function(node){
53682         var stop = this.el.dom;
53683         while(node && node != stop){
53684             if(this.findRE.test(node.className)){
53685                 return this.getCellIndex(node);
53686             }
53687             node = node.parentNode;
53688         }
53689         return false;
53690     },
53691
53692     getColumnId : function(index){
53693         return this.cm.getColumnId(index);
53694     },
53695
53696     getSplitters : function()
53697     {
53698         if(this.splitterSelector){
53699            return Roo.DomQuery.select(this.splitterSelector);
53700         }else{
53701             return null;
53702       }
53703     },
53704
53705     getSplitter : function(index){
53706         return this.getSplitters()[index];
53707     },
53708
53709     onRowOver : function(e, t){
53710         var row;
53711         if((row = this.findRowIndex(t)) !== false){
53712             this.getRowComposite(row).addClass("x-grid-row-over");
53713         }
53714     },
53715
53716     onRowOut : function(e, t){
53717         var row;
53718         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53719             this.getRowComposite(row).removeClass("x-grid-row-over");
53720         }
53721     },
53722
53723     renderHeaders : function(){
53724         var cm = this.cm;
53725         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53726         var cb = [], lb = [], sb = [], lsb = [], p = {};
53727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53728             p.cellId = "x-grid-hd-0-" + i;
53729             p.splitId = "x-grid-csplit-0-" + i;
53730             p.id = cm.getColumnId(i);
53731             p.title = cm.getColumnTooltip(i) || "";
53732             p.value = cm.getColumnHeader(i) || "";
53733             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53734             if(!cm.isLocked(i)){
53735                 cb[cb.length] = ct.apply(p);
53736                 sb[sb.length] = st.apply(p);
53737             }else{
53738                 lb[lb.length] = ct.apply(p);
53739                 lsb[lsb.length] = st.apply(p);
53740             }
53741         }
53742         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53743                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53744     },
53745
53746     updateHeaders : function(){
53747         var html = this.renderHeaders();
53748         this.lockedHd.update(html[0]);
53749         this.mainHd.update(html[1]);
53750     },
53751
53752     /**
53753      * Focuses the specified row.
53754      * @param {Number} row The row index
53755      */
53756     focusRow : function(row)
53757     {
53758         //Roo.log('GridView.focusRow');
53759         var x = this.scroller.dom.scrollLeft;
53760         this.focusCell(row, 0, false);
53761         this.scroller.dom.scrollLeft = x;
53762     },
53763
53764     /**
53765      * Focuses the specified cell.
53766      * @param {Number} row The row index
53767      * @param {Number} col The column index
53768      * @param {Boolean} hscroll false to disable horizontal scrolling
53769      */
53770     focusCell : function(row, col, hscroll)
53771     {
53772         //Roo.log('GridView.focusCell');
53773         var el = this.ensureVisible(row, col, hscroll);
53774         this.focusEl.alignTo(el, "tl-tl");
53775         if(Roo.isGecko){
53776             this.focusEl.focus();
53777         }else{
53778             this.focusEl.focus.defer(1, this.focusEl);
53779         }
53780     },
53781
53782     /**
53783      * Scrolls the specified cell into view
53784      * @param {Number} row The row index
53785      * @param {Number} col The column index
53786      * @param {Boolean} hscroll false to disable horizontal scrolling
53787      */
53788     ensureVisible : function(row, col, hscroll)
53789     {
53790         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53791         //return null; //disable for testing.
53792         if(typeof row != "number"){
53793             row = row.rowIndex;
53794         }
53795         if(row < 0 && row >= this.ds.getCount()){
53796             return  null;
53797         }
53798         col = (col !== undefined ? col : 0);
53799         var cm = this.grid.colModel;
53800         while(cm.isHidden(col)){
53801             col++;
53802         }
53803
53804         var el = this.getCell(row, col);
53805         if(!el){
53806             return null;
53807         }
53808         var c = this.scroller.dom;
53809
53810         var ctop = parseInt(el.offsetTop, 10);
53811         var cleft = parseInt(el.offsetLeft, 10);
53812         var cbot = ctop + el.offsetHeight;
53813         var cright = cleft + el.offsetWidth;
53814         
53815         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53816         var stop = parseInt(c.scrollTop, 10);
53817         var sleft = parseInt(c.scrollLeft, 10);
53818         var sbot = stop + ch;
53819         var sright = sleft + c.clientWidth;
53820         /*
53821         Roo.log('GridView.ensureVisible:' +
53822                 ' ctop:' + ctop +
53823                 ' c.clientHeight:' + c.clientHeight +
53824                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53825                 ' stop:' + stop +
53826                 ' cbot:' + cbot +
53827                 ' sbot:' + sbot +
53828                 ' ch:' + ch  
53829                 );
53830         */
53831         if(ctop < stop){
53832              c.scrollTop = ctop;
53833             //Roo.log("set scrolltop to ctop DISABLE?");
53834         }else if(cbot > sbot){
53835             //Roo.log("set scrolltop to cbot-ch");
53836             c.scrollTop = cbot-ch;
53837         }
53838         
53839         if(hscroll !== false){
53840             if(cleft < sleft){
53841                 c.scrollLeft = cleft;
53842             }else if(cright > sright){
53843                 c.scrollLeft = cright-c.clientWidth;
53844             }
53845         }
53846          
53847         return el;
53848     },
53849
53850     updateColumns : function(){
53851         this.grid.stopEditing();
53852         var cm = this.grid.colModel, colIds = this.getColumnIds();
53853         //var totalWidth = cm.getTotalWidth();
53854         var pos = 0;
53855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53856             //if(cm.isHidden(i)) continue;
53857             var w = cm.getColumnWidth(i);
53858             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53859             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53860         }
53861         this.updateSplitters();
53862     },
53863
53864     generateRules : function(cm){
53865         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53866         Roo.util.CSS.removeStyleSheet(rulesId);
53867         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53868             var cid = cm.getColumnId(i);
53869             var align = '';
53870             if(cm.config[i].align){
53871                 align = 'text-align:'+cm.config[i].align+';';
53872             }
53873             var hidden = '';
53874             if(cm.isHidden(i)){
53875                 hidden = 'display:none;';
53876             }
53877             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53878             ruleBuf.push(
53879                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53880                     this.hdSelector, cid, " {\n", align, width, "}\n",
53881                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53882                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53883         }
53884         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53885     },
53886
53887     updateSplitters : function(){
53888         var cm = this.cm, s = this.getSplitters();
53889         if(s){ // splitters not created yet
53890             var pos = 0, locked = true;
53891             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53892                 if(cm.isHidden(i)) continue;
53893                 var w = cm.getColumnWidth(i); // make sure it's a number
53894                 if(!cm.isLocked(i) && locked){
53895                     pos = 0;
53896                     locked = false;
53897                 }
53898                 pos += w;
53899                 s[i].style.left = (pos-this.splitOffset) + "px";
53900             }
53901         }
53902     },
53903
53904     handleHiddenChange : function(colModel, colIndex, hidden){
53905         if(hidden){
53906             this.hideColumn(colIndex);
53907         }else{
53908             this.unhideColumn(colIndex);
53909         }
53910     },
53911
53912     hideColumn : function(colIndex){
53913         var cid = this.getColumnId(colIndex);
53914         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53915         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53916         if(Roo.isSafari){
53917             this.updateHeaders();
53918         }
53919         this.updateSplitters();
53920         this.layout();
53921     },
53922
53923     unhideColumn : function(colIndex){
53924         var cid = this.getColumnId(colIndex);
53925         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53926         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53927
53928         if(Roo.isSafari){
53929             this.updateHeaders();
53930         }
53931         this.updateSplitters();
53932         this.layout();
53933     },
53934
53935     insertRows : function(dm, firstRow, lastRow, isUpdate){
53936         if(firstRow == 0 && lastRow == dm.getCount()-1){
53937             this.refresh();
53938         }else{
53939             if(!isUpdate){
53940                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53941             }
53942             var s = this.getScrollState();
53943             var markup = this.renderRows(firstRow, lastRow);
53944             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53945             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53946             this.restoreScroll(s);
53947             if(!isUpdate){
53948                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53949                 this.syncRowHeights(firstRow, lastRow);
53950                 this.stripeRows(firstRow);
53951                 this.layout();
53952             }
53953         }
53954     },
53955
53956     bufferRows : function(markup, target, index){
53957         var before = null, trows = target.rows, tbody = target.tBodies[0];
53958         if(index < trows.length){
53959             before = trows[index];
53960         }
53961         var b = document.createElement("div");
53962         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53963         var rows = b.firstChild.rows;
53964         for(var i = 0, len = rows.length; i < len; i++){
53965             if(before){
53966                 tbody.insertBefore(rows[0], before);
53967             }else{
53968                 tbody.appendChild(rows[0]);
53969             }
53970         }
53971         b.innerHTML = "";
53972         b = null;
53973     },
53974
53975     deleteRows : function(dm, firstRow, lastRow){
53976         if(dm.getRowCount()<1){
53977             this.fireEvent("beforerefresh", this);
53978             this.mainBody.update("");
53979             this.lockedBody.update("");
53980             this.fireEvent("refresh", this);
53981         }else{
53982             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53983             var bt = this.getBodyTable();
53984             var tbody = bt.firstChild;
53985             var rows = bt.rows;
53986             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53987                 tbody.removeChild(rows[firstRow]);
53988             }
53989             this.stripeRows(firstRow);
53990             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53991         }
53992     },
53993
53994     updateRows : function(dataSource, firstRow, lastRow){
53995         var s = this.getScrollState();
53996         this.refresh();
53997         this.restoreScroll(s);
53998     },
53999
54000     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54001         if(!noRefresh){
54002            this.refresh();
54003         }
54004         this.updateHeaderSortState();
54005     },
54006
54007     getScrollState : function(){
54008         
54009         var sb = this.scroller.dom;
54010         return {left: sb.scrollLeft, top: sb.scrollTop};
54011     },
54012
54013     stripeRows : function(startRow){
54014         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54015             return;
54016         }
54017         startRow = startRow || 0;
54018         var rows = this.getBodyTable().rows;
54019         var lrows = this.getLockedTable().rows;
54020         var cls = ' x-grid-row-alt ';
54021         for(var i = startRow, len = rows.length; i < len; i++){
54022             var row = rows[i], lrow = lrows[i];
54023             var isAlt = ((i+1) % 2 == 0);
54024             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54025             if(isAlt == hasAlt){
54026                 continue;
54027             }
54028             if(isAlt){
54029                 row.className += " x-grid-row-alt";
54030             }else{
54031                 row.className = row.className.replace("x-grid-row-alt", "");
54032             }
54033             if(lrow){
54034                 lrow.className = row.className;
54035             }
54036         }
54037     },
54038
54039     restoreScroll : function(state){
54040         //Roo.log('GridView.restoreScroll');
54041         var sb = this.scroller.dom;
54042         sb.scrollLeft = state.left;
54043         sb.scrollTop = state.top;
54044         this.syncScroll();
54045     },
54046
54047     syncScroll : function(){
54048         //Roo.log('GridView.syncScroll');
54049         var sb = this.scroller.dom;
54050         var sh = this.mainHd.dom;
54051         var bs = this.mainBody.dom;
54052         var lv = this.lockedBody.dom;
54053         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54054         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54055     },
54056
54057     handleScroll : function(e){
54058         this.syncScroll();
54059         var sb = this.scroller.dom;
54060         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54061         e.stopEvent();
54062     },
54063
54064     handleWheel : function(e){
54065         var d = e.getWheelDelta();
54066         this.scroller.dom.scrollTop -= d*22;
54067         // set this here to prevent jumpy scrolling on large tables
54068         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54069         e.stopEvent();
54070     },
54071
54072     renderRows : function(startRow, endRow){
54073         // pull in all the crap needed to render rows
54074         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54075         var colCount = cm.getColumnCount();
54076
54077         if(ds.getCount() < 1){
54078             return ["", ""];
54079         }
54080
54081         // build a map for all the columns
54082         var cs = [];
54083         for(var i = 0; i < colCount; i++){
54084             var name = cm.getDataIndex(i);
54085             cs[i] = {
54086                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54087                 renderer : cm.getRenderer(i),
54088                 id : cm.getColumnId(i),
54089                 locked : cm.isLocked(i)
54090             };
54091         }
54092
54093         startRow = startRow || 0;
54094         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54095
54096         // records to render
54097         var rs = ds.getRange(startRow, endRow);
54098
54099         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54100     },
54101
54102     // As much as I hate to duplicate code, this was branched because FireFox really hates
54103     // [].join("") on strings. The performance difference was substantial enough to
54104     // branch this function
54105     doRender : Roo.isGecko ?
54106             function(cs, rs, ds, startRow, colCount, stripe){
54107                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54108                 // buffers
54109                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54110                 
54111                 var hasListener = this.grid.hasListener('rowclass');
54112                 var rowcfg = {};
54113                 for(var j = 0, len = rs.length; j < len; j++){
54114                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54115                     for(var i = 0; i < colCount; i++){
54116                         c = cs[i];
54117                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54118                         p.id = c.id;
54119                         p.css = p.attr = "";
54120                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54121                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54122                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54123                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54124                         }
54125                         var markup = ct.apply(p);
54126                         if(!c.locked){
54127                             cb+= markup;
54128                         }else{
54129                             lcb+= markup;
54130                         }
54131                     }
54132                     var alt = [];
54133                     if(stripe && ((rowIndex+1) % 2 == 0)){
54134                         alt.push("x-grid-row-alt")
54135                     }
54136                     if(r.dirty){
54137                         alt.push(  " x-grid-dirty-row");
54138                     }
54139                     rp.cells = lcb;
54140                     if(this.getRowClass){
54141                         alt.push(this.getRowClass(r, rowIndex));
54142                     }
54143                     if (hasListener) {
54144                         rowcfg = {
54145                              
54146                             record: r,
54147                             rowIndex : rowIndex,
54148                             rowClass : ''
54149                         }
54150                         this.grid.fireEvent('rowclass', this, rowcfg);
54151                         alt.push(rowcfg.rowClass);
54152                     }
54153                     rp.alt = alt.join(" ");
54154                     lbuf+= rt.apply(rp);
54155                     rp.cells = cb;
54156                     buf+=  rt.apply(rp);
54157                 }
54158                 return [lbuf, buf];
54159             } :
54160             function(cs, rs, ds, startRow, colCount, stripe){
54161                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54162                 // buffers
54163                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54164                 var hasListener = this.grid.hasListener('rowclass');
54165  
54166                 var rowcfg = {};
54167                 for(var j = 0, len = rs.length; j < len; j++){
54168                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54169                     for(var i = 0; i < colCount; i++){
54170                         c = cs[i];
54171                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54172                         p.id = c.id;
54173                         p.css = p.attr = "";
54174                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54175                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54176                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54177                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54178                         }
54179                         
54180                         var markup = ct.apply(p);
54181                         if(!c.locked){
54182                             cb[cb.length] = markup;
54183                         }else{
54184                             lcb[lcb.length] = markup;
54185                         }
54186                     }
54187                     var alt = [];
54188                     if(stripe && ((rowIndex+1) % 2 == 0)){
54189                         alt.push( "x-grid-row-alt");
54190                     }
54191                     if(r.dirty){
54192                         alt.push(" x-grid-dirty-row");
54193                     }
54194                     rp.cells = lcb;
54195                     if(this.getRowClass){
54196                         alt.push( this.getRowClass(r, rowIndex));
54197                     }
54198                     if (hasListener) {
54199                         rowcfg = {
54200                              
54201                             record: r,
54202                             rowIndex : rowIndex,
54203                             rowClass : ''
54204                         }
54205                         this.grid.fireEvent('rowclass', this, rowcfg);
54206                         alt.push(rowcfg.rowClass);
54207                     }
54208                     rp.alt = alt.join(" ");
54209                     rp.cells = lcb.join("");
54210                     lbuf[lbuf.length] = rt.apply(rp);
54211                     rp.cells = cb.join("");
54212                     buf[buf.length] =  rt.apply(rp);
54213                 }
54214                 return [lbuf.join(""), buf.join("")];
54215             },
54216
54217     renderBody : function(){
54218         var markup = this.renderRows();
54219         var bt = this.templates.body;
54220         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54221     },
54222
54223     /**
54224      * Refreshes the grid
54225      * @param {Boolean} headersToo
54226      */
54227     refresh : function(headersToo){
54228         this.fireEvent("beforerefresh", this);
54229         this.grid.stopEditing();
54230         var result = this.renderBody();
54231         this.lockedBody.update(result[0]);
54232         this.mainBody.update(result[1]);
54233         if(headersToo === true){
54234             this.updateHeaders();
54235             this.updateColumns();
54236             this.updateSplitters();
54237             this.updateHeaderSortState();
54238         }
54239         this.syncRowHeights();
54240         this.layout();
54241         this.fireEvent("refresh", this);
54242     },
54243
54244     handleColumnMove : function(cm, oldIndex, newIndex){
54245         this.indexMap = null;
54246         var s = this.getScrollState();
54247         this.refresh(true);
54248         this.restoreScroll(s);
54249         this.afterMove(newIndex);
54250     },
54251
54252     afterMove : function(colIndex){
54253         if(this.enableMoveAnim && Roo.enableFx){
54254             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54255         }
54256         // if multisort - fix sortOrder, and reload..
54257         if (this.grid.dataSource.multiSort) {
54258             // the we can call sort again..
54259             var dm = this.grid.dataSource;
54260             var cm = this.grid.colModel;
54261             var so = [];
54262             for(var i = 0; i < cm.config.length; i++ ) {
54263                 
54264                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54265                     continue; // dont' bother, it's not in sort list or being set.
54266                 }
54267                 
54268                 so.push(cm.config[i].dataIndex);
54269             };
54270             dm.sortOrder = so;
54271             dm.load(dm.lastOptions);
54272             
54273             
54274         }
54275         
54276     },
54277
54278     updateCell : function(dm, rowIndex, dataIndex){
54279         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54280         if(typeof colIndex == "undefined"){ // not present in grid
54281             return;
54282         }
54283         var cm = this.grid.colModel;
54284         var cell = this.getCell(rowIndex, colIndex);
54285         var cellText = this.getCellText(rowIndex, colIndex);
54286
54287         var p = {
54288             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54289             id : cm.getColumnId(colIndex),
54290             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54291         };
54292         var renderer = cm.getRenderer(colIndex);
54293         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54294         if(typeof val == "undefined" || val === "") val = "&#160;";
54295         cellText.innerHTML = val;
54296         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54297         this.syncRowHeights(rowIndex, rowIndex);
54298     },
54299
54300     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54301         var maxWidth = 0;
54302         if(this.grid.autoSizeHeaders){
54303             var h = this.getHeaderCellMeasure(colIndex);
54304             maxWidth = Math.max(maxWidth, h.scrollWidth);
54305         }
54306         var tb, index;
54307         if(this.cm.isLocked(colIndex)){
54308             tb = this.getLockedTable();
54309             index = colIndex;
54310         }else{
54311             tb = this.getBodyTable();
54312             index = colIndex - this.cm.getLockedCount();
54313         }
54314         if(tb && tb.rows){
54315             var rows = tb.rows;
54316             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54317             for(var i = 0; i < stopIndex; i++){
54318                 var cell = rows[i].childNodes[index].firstChild;
54319                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54320             }
54321         }
54322         return maxWidth + /*margin for error in IE*/ 5;
54323     },
54324     /**
54325      * Autofit a column to its content.
54326      * @param {Number} colIndex
54327      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54328      */
54329      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54330          if(this.cm.isHidden(colIndex)){
54331              return; // can't calc a hidden column
54332          }
54333         if(forceMinSize){
54334             var cid = this.cm.getColumnId(colIndex);
54335             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54336            if(this.grid.autoSizeHeaders){
54337                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54338            }
54339         }
54340         var newWidth = this.calcColumnWidth(colIndex);
54341         this.cm.setColumnWidth(colIndex,
54342             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54343         if(!suppressEvent){
54344             this.grid.fireEvent("columnresize", colIndex, newWidth);
54345         }
54346     },
54347
54348     /**
54349      * Autofits all columns to their content and then expands to fit any extra space in the grid
54350      */
54351      autoSizeColumns : function(){
54352         var cm = this.grid.colModel;
54353         var colCount = cm.getColumnCount();
54354         for(var i = 0; i < colCount; i++){
54355             this.autoSizeColumn(i, true, true);
54356         }
54357         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54358             this.fitColumns();
54359         }else{
54360             this.updateColumns();
54361             this.layout();
54362         }
54363     },
54364
54365     /**
54366      * Autofits all columns to the grid's width proportionate with their current size
54367      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54368      */
54369     fitColumns : function(reserveScrollSpace){
54370         var cm = this.grid.colModel;
54371         var colCount = cm.getColumnCount();
54372         var cols = [];
54373         var width = 0;
54374         var i, w;
54375         for (i = 0; i < colCount; i++){
54376             if(!cm.isHidden(i) && !cm.isFixed(i)){
54377                 w = cm.getColumnWidth(i);
54378                 cols.push(i);
54379                 cols.push(w);
54380                 width += w;
54381             }
54382         }
54383         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54384         if(reserveScrollSpace){
54385             avail -= 17;
54386         }
54387         var frac = (avail - cm.getTotalWidth())/width;
54388         while (cols.length){
54389             w = cols.pop();
54390             i = cols.pop();
54391             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54392         }
54393         this.updateColumns();
54394         this.layout();
54395     },
54396
54397     onRowSelect : function(rowIndex){
54398         var row = this.getRowComposite(rowIndex);
54399         row.addClass("x-grid-row-selected");
54400     },
54401
54402     onRowDeselect : function(rowIndex){
54403         var row = this.getRowComposite(rowIndex);
54404         row.removeClass("x-grid-row-selected");
54405     },
54406
54407     onCellSelect : function(row, col){
54408         var cell = this.getCell(row, col);
54409         if(cell){
54410             Roo.fly(cell).addClass("x-grid-cell-selected");
54411         }
54412     },
54413
54414     onCellDeselect : function(row, col){
54415         var cell = this.getCell(row, col);
54416         if(cell){
54417             Roo.fly(cell).removeClass("x-grid-cell-selected");
54418         }
54419     },
54420
54421     updateHeaderSortState : function(){
54422         
54423         // sort state can be single { field: xxx, direction : yyy}
54424         // or   { xxx=>ASC , yyy : DESC ..... }
54425         
54426         var mstate = {};
54427         if (!this.ds.multiSort) { 
54428             var state = this.ds.getSortState();
54429             if(!state){
54430                 return;
54431             }
54432             mstate[state.field] = state.direction;
54433             // FIXME... - this is not used here.. but might be elsewhere..
54434             this.sortState = state;
54435             
54436         } else {
54437             mstate = this.ds.sortToggle;
54438         }
54439         //remove existing sort classes..
54440         
54441         var sc = this.sortClasses;
54442         var hds = this.el.select(this.headerSelector).removeClass(sc);
54443         
54444         for(var f in mstate) {
54445         
54446             var sortColumn = this.cm.findColumnIndex(f);
54447             
54448             if(sortColumn != -1){
54449                 var sortDir = mstate[f];        
54450                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54451             }
54452         }
54453         
54454          
54455         
54456     },
54457
54458
54459     handleHeaderClick : function(g, index,e){
54460         
54461         Roo.log("header click");
54462         
54463         if (Roo.isTouch) {
54464             // touch events on header are handled by context
54465             this.handleHdCtx(g,index,e);
54466             return;
54467         }
54468         
54469         
54470         if(this.headersDisabled){
54471             return;
54472         }
54473         var dm = g.dataSource, cm = g.colModel;
54474         if(!cm.isSortable(index)){
54475             return;
54476         }
54477         g.stopEditing();
54478         
54479         if (dm.multiSort) {
54480             // update the sortOrder
54481             var so = [];
54482             for(var i = 0; i < cm.config.length; i++ ) {
54483                 
54484                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54485                     continue; // dont' bother, it's not in sort list or being set.
54486                 }
54487                 
54488                 so.push(cm.config[i].dataIndex);
54489             };
54490             dm.sortOrder = so;
54491         }
54492         
54493         
54494         dm.sort(cm.getDataIndex(index));
54495     },
54496
54497
54498     destroy : function(){
54499         if(this.colMenu){
54500             this.colMenu.removeAll();
54501             Roo.menu.MenuMgr.unregister(this.colMenu);
54502             this.colMenu.getEl().remove();
54503             delete this.colMenu;
54504         }
54505         if(this.hmenu){
54506             this.hmenu.removeAll();
54507             Roo.menu.MenuMgr.unregister(this.hmenu);
54508             this.hmenu.getEl().remove();
54509             delete this.hmenu;
54510         }
54511         if(this.grid.enableColumnMove){
54512             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54513             if(dds){
54514                 for(var dd in dds){
54515                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54516                         var elid = dds[dd].dragElId;
54517                         dds[dd].unreg();
54518                         Roo.get(elid).remove();
54519                     } else if(dds[dd].config.isTarget){
54520                         dds[dd].proxyTop.remove();
54521                         dds[dd].proxyBottom.remove();
54522                         dds[dd].unreg();
54523                     }
54524                     if(Roo.dd.DDM.locationCache[dd]){
54525                         delete Roo.dd.DDM.locationCache[dd];
54526                     }
54527                 }
54528                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54529             }
54530         }
54531         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54532         this.bind(null, null);
54533         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54534     },
54535
54536     handleLockChange : function(){
54537         this.refresh(true);
54538     },
54539
54540     onDenyColumnLock : function(){
54541
54542     },
54543
54544     onDenyColumnHide : function(){
54545
54546     },
54547
54548     handleHdMenuClick : function(item){
54549         var index = this.hdCtxIndex;
54550         var cm = this.cm, ds = this.ds;
54551         switch(item.id){
54552             case "asc":
54553                 ds.sort(cm.getDataIndex(index), "ASC");
54554                 break;
54555             case "desc":
54556                 ds.sort(cm.getDataIndex(index), "DESC");
54557                 break;
54558             case "lock":
54559                 var lc = cm.getLockedCount();
54560                 if(cm.getColumnCount(true) <= lc+1){
54561                     this.onDenyColumnLock();
54562                     return;
54563                 }
54564                 if(lc != index){
54565                     cm.setLocked(index, true, true);
54566                     cm.moveColumn(index, lc);
54567                     this.grid.fireEvent("columnmove", index, lc);
54568                 }else{
54569                     cm.setLocked(index, true);
54570                 }
54571             break;
54572             case "unlock":
54573                 var lc = cm.getLockedCount();
54574                 if((lc-1) != index){
54575                     cm.setLocked(index, false, true);
54576                     cm.moveColumn(index, lc-1);
54577                     this.grid.fireEvent("columnmove", index, lc-1);
54578                 }else{
54579                     cm.setLocked(index, false);
54580                 }
54581             break;
54582             case 'wider': // used to expand cols on touch..
54583             case 'narrow':
54584                 var cw = cm.getColumnWidth(index);
54585                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54586                 cw = Math.max(0, cw);
54587                 cw = Math.min(cw,4000);
54588                 cm.setColumnWidth(index, cw);
54589                 break;
54590                 
54591             default:
54592                 index = cm.getIndexById(item.id.substr(4));
54593                 if(index != -1){
54594                     if(item.checked && cm.getColumnCount(true) <= 1){
54595                         this.onDenyColumnHide();
54596                         return false;
54597                     }
54598                     cm.setHidden(index, item.checked);
54599                 }
54600         }
54601         return true;
54602     },
54603
54604     beforeColMenuShow : function(){
54605         var cm = this.cm,  colCount = cm.getColumnCount();
54606         this.colMenu.removeAll();
54607         for(var i = 0; i < colCount; i++){
54608             this.colMenu.add(new Roo.menu.CheckItem({
54609                 id: "col-"+cm.getColumnId(i),
54610                 text: cm.getColumnHeader(i),
54611                 checked: !cm.isHidden(i),
54612                 hideOnClick:false
54613             }));
54614         }
54615     },
54616
54617     handleHdCtx : function(g, index, e){
54618         e.stopEvent();
54619         var hd = this.getHeaderCell(index);
54620         this.hdCtxIndex = index;
54621         var ms = this.hmenu.items, cm = this.cm;
54622         ms.get("asc").setDisabled(!cm.isSortable(index));
54623         ms.get("desc").setDisabled(!cm.isSortable(index));
54624         if(this.grid.enableColLock !== false){
54625             ms.get("lock").setDisabled(cm.isLocked(index));
54626             ms.get("unlock").setDisabled(!cm.isLocked(index));
54627         }
54628         this.hmenu.show(hd, "tl-bl");
54629     },
54630
54631     handleHdOver : function(e){
54632         var hd = this.findHeaderCell(e.getTarget());
54633         if(hd && !this.headersDisabled){
54634             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54635                this.fly(hd).addClass("x-grid-hd-over");
54636             }
54637         }
54638     },
54639
54640     handleHdOut : function(e){
54641         var hd = this.findHeaderCell(e.getTarget());
54642         if(hd){
54643             this.fly(hd).removeClass("x-grid-hd-over");
54644         }
54645     },
54646
54647     handleSplitDblClick : function(e, t){
54648         var i = this.getCellIndex(t);
54649         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54650             this.autoSizeColumn(i, true);
54651             this.layout();
54652         }
54653     },
54654
54655     render : function(){
54656
54657         var cm = this.cm;
54658         var colCount = cm.getColumnCount();
54659
54660         if(this.grid.monitorWindowResize === true){
54661             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54662         }
54663         var header = this.renderHeaders();
54664         var body = this.templates.body.apply({rows:""});
54665         var html = this.templates.master.apply({
54666             lockedBody: body,
54667             body: body,
54668             lockedHeader: header[0],
54669             header: header[1]
54670         });
54671
54672         //this.updateColumns();
54673
54674         this.grid.getGridEl().dom.innerHTML = html;
54675
54676         this.initElements();
54677         
54678         // a kludge to fix the random scolling effect in webkit
54679         this.el.on("scroll", function() {
54680             this.el.dom.scrollTop=0; // hopefully not recursive..
54681         },this);
54682
54683         this.scroller.on("scroll", this.handleScroll, this);
54684         this.lockedBody.on("mousewheel", this.handleWheel, this);
54685         this.mainBody.on("mousewheel", this.handleWheel, this);
54686
54687         this.mainHd.on("mouseover", this.handleHdOver, this);
54688         this.mainHd.on("mouseout", this.handleHdOut, this);
54689         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54690                 {delegate: "."+this.splitClass});
54691
54692         this.lockedHd.on("mouseover", this.handleHdOver, this);
54693         this.lockedHd.on("mouseout", this.handleHdOut, this);
54694         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54695                 {delegate: "."+this.splitClass});
54696
54697         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54698             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54699         }
54700
54701         this.updateSplitters();
54702
54703         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54704             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54705             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54706         }
54707
54708         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54709             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54710             this.hmenu.add(
54711                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54712                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54713             );
54714             if(this.grid.enableColLock !== false){
54715                 this.hmenu.add('-',
54716                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54717                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54718                 );
54719             }
54720             if (Roo.isTouch) {
54721                  this.hmenu.add('-',
54722                     {id:"wider", text: this.columnsWiderText},
54723                     {id:"narrow", text: this.columnsNarrowText }
54724                 );
54725                 
54726                  
54727             }
54728             
54729             if(this.grid.enableColumnHide !== false){
54730
54731                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54732                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54733                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54734
54735                 this.hmenu.add('-',
54736                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54737                 );
54738             }
54739             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54740
54741             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54742         }
54743
54744         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54745             this.dd = new Roo.grid.GridDragZone(this.grid, {
54746                 ddGroup : this.grid.ddGroup || 'GridDD'
54747             });
54748             
54749         }
54750
54751         /*
54752         for(var i = 0; i < colCount; i++){
54753             if(cm.isHidden(i)){
54754                 this.hideColumn(i);
54755             }
54756             if(cm.config[i].align){
54757                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54758                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54759             }
54760         }*/
54761         
54762         this.updateHeaderSortState();
54763
54764         this.beforeInitialResize();
54765         this.layout(true);
54766
54767         // two part rendering gives faster view to the user
54768         this.renderPhase2.defer(1, this);
54769     },
54770
54771     renderPhase2 : function(){
54772         // render the rows now
54773         this.refresh();
54774         if(this.grid.autoSizeColumns){
54775             this.autoSizeColumns();
54776         }
54777     },
54778
54779     beforeInitialResize : function(){
54780
54781     },
54782
54783     onColumnSplitterMoved : function(i, w){
54784         this.userResized = true;
54785         var cm = this.grid.colModel;
54786         cm.setColumnWidth(i, w, true);
54787         var cid = cm.getColumnId(i);
54788         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54789         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54790         this.updateSplitters();
54791         this.layout();
54792         this.grid.fireEvent("columnresize", i, w);
54793     },
54794
54795     syncRowHeights : function(startIndex, endIndex){
54796         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54797             startIndex = startIndex || 0;
54798             var mrows = this.getBodyTable().rows;
54799             var lrows = this.getLockedTable().rows;
54800             var len = mrows.length-1;
54801             endIndex = Math.min(endIndex || len, len);
54802             for(var i = startIndex; i <= endIndex; i++){
54803                 var m = mrows[i], l = lrows[i];
54804                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54805                 m.style.height = l.style.height = h + "px";
54806             }
54807         }
54808     },
54809
54810     layout : function(initialRender, is2ndPass){
54811         var g = this.grid;
54812         var auto = g.autoHeight;
54813         var scrollOffset = 16;
54814         var c = g.getGridEl(), cm = this.cm,
54815                 expandCol = g.autoExpandColumn,
54816                 gv = this;
54817         //c.beginMeasure();
54818
54819         if(!c.dom.offsetWidth){ // display:none?
54820             if(initialRender){
54821                 this.lockedWrap.show();
54822                 this.mainWrap.show();
54823             }
54824             return;
54825         }
54826
54827         var hasLock = this.cm.isLocked(0);
54828
54829         var tbh = this.headerPanel.getHeight();
54830         var bbh = this.footerPanel.getHeight();
54831
54832         if(auto){
54833             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54834             var newHeight = ch + c.getBorderWidth("tb");
54835             if(g.maxHeight){
54836                 newHeight = Math.min(g.maxHeight, newHeight);
54837             }
54838             c.setHeight(newHeight);
54839         }
54840
54841         if(g.autoWidth){
54842             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54843         }
54844
54845         var s = this.scroller;
54846
54847         var csize = c.getSize(true);
54848
54849         this.el.setSize(csize.width, csize.height);
54850
54851         this.headerPanel.setWidth(csize.width);
54852         this.footerPanel.setWidth(csize.width);
54853
54854         var hdHeight = this.mainHd.getHeight();
54855         var vw = csize.width;
54856         var vh = csize.height - (tbh + bbh);
54857
54858         s.setSize(vw, vh);
54859
54860         var bt = this.getBodyTable();
54861         var ltWidth = hasLock ?
54862                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54863
54864         var scrollHeight = bt.offsetHeight;
54865         var scrollWidth = ltWidth + bt.offsetWidth;
54866         var vscroll = false, hscroll = false;
54867
54868         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54869
54870         var lw = this.lockedWrap, mw = this.mainWrap;
54871         var lb = this.lockedBody, mb = this.mainBody;
54872
54873         setTimeout(function(){
54874             var t = s.dom.offsetTop;
54875             var w = s.dom.clientWidth,
54876                 h = s.dom.clientHeight;
54877
54878             lw.setTop(t);
54879             lw.setSize(ltWidth, h);
54880
54881             mw.setLeftTop(ltWidth, t);
54882             mw.setSize(w-ltWidth, h);
54883
54884             lb.setHeight(h-hdHeight);
54885             mb.setHeight(h-hdHeight);
54886
54887             if(is2ndPass !== true && !gv.userResized && expandCol){
54888                 // high speed resize without full column calculation
54889                 
54890                 var ci = cm.getIndexById(expandCol);
54891                 if (ci < 0) {
54892                     ci = cm.findColumnIndex(expandCol);
54893                 }
54894                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54895                 var expandId = cm.getColumnId(ci);
54896                 var  tw = cm.getTotalWidth(false);
54897                 var currentWidth = cm.getColumnWidth(ci);
54898                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54899                 if(currentWidth != cw){
54900                     cm.setColumnWidth(ci, cw, true);
54901                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54902                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54903                     gv.updateSplitters();
54904                     gv.layout(false, true);
54905                 }
54906             }
54907
54908             if(initialRender){
54909                 lw.show();
54910                 mw.show();
54911             }
54912             //c.endMeasure();
54913         }, 10);
54914     },
54915
54916     onWindowResize : function(){
54917         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54918             return;
54919         }
54920         this.layout();
54921     },
54922
54923     appendFooter : function(parentEl){
54924         return null;
54925     },
54926
54927     sortAscText : "Sort Ascending",
54928     sortDescText : "Sort Descending",
54929     lockText : "Lock Column",
54930     unlockText : "Unlock Column",
54931     columnsText : "Columns",
54932  
54933     columnsWiderText : "Wider",
54934     columnsNarrowText : "Thinner"
54935 });
54936
54937
54938 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54939     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54940     this.proxy.el.addClass('x-grid3-col-dd');
54941 };
54942
54943 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54944     handleMouseDown : function(e){
54945
54946     },
54947
54948     callHandleMouseDown : function(e){
54949         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54950     }
54951 });
54952 /*
54953  * Based on:
54954  * Ext JS Library 1.1.1
54955  * Copyright(c) 2006-2007, Ext JS, LLC.
54956  *
54957  * Originally Released Under LGPL - original licence link has changed is not relivant.
54958  *
54959  * Fork - LGPL
54960  * <script type="text/javascript">
54961  */
54962  
54963 // private
54964 // This is a support class used internally by the Grid components
54965 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54966     this.grid = grid;
54967     this.view = grid.getView();
54968     this.proxy = this.view.resizeProxy;
54969     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54970         "gridSplitters" + this.grid.getGridEl().id, {
54971         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54972     });
54973     this.setHandleElId(Roo.id(hd));
54974     this.setOuterHandleElId(Roo.id(hd2));
54975     this.scroll = false;
54976 };
54977 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54978     fly: Roo.Element.fly,
54979
54980     b4StartDrag : function(x, y){
54981         this.view.headersDisabled = true;
54982         this.proxy.setHeight(this.view.mainWrap.getHeight());
54983         var w = this.cm.getColumnWidth(this.cellIndex);
54984         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54985         this.resetConstraints();
54986         this.setXConstraint(minw, 1000);
54987         this.setYConstraint(0, 0);
54988         this.minX = x - minw;
54989         this.maxX = x + 1000;
54990         this.startPos = x;
54991         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54992     },
54993
54994
54995     handleMouseDown : function(e){
54996         ev = Roo.EventObject.setEvent(e);
54997         var t = this.fly(ev.getTarget());
54998         if(t.hasClass("x-grid-split")){
54999             this.cellIndex = this.view.getCellIndex(t.dom);
55000             this.split = t.dom;
55001             this.cm = this.grid.colModel;
55002             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55003                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55004             }
55005         }
55006     },
55007
55008     endDrag : function(e){
55009         this.view.headersDisabled = false;
55010         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55011         var diff = endX - this.startPos;
55012         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55013     },
55014
55015     autoOffset : function(){
55016         this.setDelta(0,0);
55017     }
55018 });/*
55019  * Based on:
55020  * Ext JS Library 1.1.1
55021  * Copyright(c) 2006-2007, Ext JS, LLC.
55022  *
55023  * Originally Released Under LGPL - original licence link has changed is not relivant.
55024  *
55025  * Fork - LGPL
55026  * <script type="text/javascript">
55027  */
55028  
55029 // private
55030 // This is a support class used internally by the Grid components
55031 Roo.grid.GridDragZone = function(grid, config){
55032     this.view = grid.getView();
55033     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55034     if(this.view.lockedBody){
55035         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55036         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55037     }
55038     this.scroll = false;
55039     this.grid = grid;
55040     this.ddel = document.createElement('div');
55041     this.ddel.className = 'x-grid-dd-wrap';
55042 };
55043
55044 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55045     ddGroup : "GridDD",
55046
55047     getDragData : function(e){
55048         var t = Roo.lib.Event.getTarget(e);
55049         var rowIndex = this.view.findRowIndex(t);
55050         var sm = this.grid.selModel;
55051             
55052         //Roo.log(rowIndex);
55053         
55054         if (sm.getSelectedCell) {
55055             // cell selection..
55056             if (!sm.getSelectedCell()) {
55057                 return false;
55058             }
55059             if (rowIndex != sm.getSelectedCell()[0]) {
55060                 return false;
55061             }
55062         
55063         }
55064         
55065         if(rowIndex !== false){
55066             
55067             // if editorgrid.. 
55068             
55069             
55070             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55071                
55072             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55073               //  
55074             //}
55075             if (e.hasModifier()){
55076                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55077             }
55078             
55079             Roo.log("getDragData");
55080             
55081             return {
55082                 grid: this.grid,
55083                 ddel: this.ddel,
55084                 rowIndex: rowIndex,
55085                 selections:sm.getSelections ? sm.getSelections() : (
55086                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55087                 )
55088             };
55089         }
55090         return false;
55091     },
55092
55093     onInitDrag : function(e){
55094         var data = this.dragData;
55095         this.ddel.innerHTML = this.grid.getDragDropText();
55096         this.proxy.update(this.ddel);
55097         // fire start drag?
55098     },
55099
55100     afterRepair : function(){
55101         this.dragging = false;
55102     },
55103
55104     getRepairXY : function(e, data){
55105         return false;
55106     },
55107
55108     onEndDrag : function(data, e){
55109         // fire end drag?
55110     },
55111
55112     onValidDrop : function(dd, e, id){
55113         // fire drag drop?
55114         this.hideProxy();
55115     },
55116
55117     beforeInvalidDrop : function(e, id){
55118
55119     }
55120 });/*
55121  * Based on:
55122  * Ext JS Library 1.1.1
55123  * Copyright(c) 2006-2007, Ext JS, LLC.
55124  *
55125  * Originally Released Under LGPL - original licence link has changed is not relivant.
55126  *
55127  * Fork - LGPL
55128  * <script type="text/javascript">
55129  */
55130  
55131
55132 /**
55133  * @class Roo.grid.ColumnModel
55134  * @extends Roo.util.Observable
55135  * This is the default implementation of a ColumnModel used by the Grid. It defines
55136  * the columns in the grid.
55137  * <br>Usage:<br>
55138  <pre><code>
55139  var colModel = new Roo.grid.ColumnModel([
55140         {header: "Ticker", width: 60, sortable: true, locked: true},
55141         {header: "Company Name", width: 150, sortable: true},
55142         {header: "Market Cap.", width: 100, sortable: true},
55143         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55144         {header: "Employees", width: 100, sortable: true, resizable: false}
55145  ]);
55146  </code></pre>
55147  * <p>
55148  
55149  * The config options listed for this class are options which may appear in each
55150  * individual column definition.
55151  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55152  * @constructor
55153  * @param {Object} config An Array of column config objects. See this class's
55154  * config objects for details.
55155 */
55156 Roo.grid.ColumnModel = function(config){
55157         /**
55158      * The config passed into the constructor
55159      */
55160     this.config = config;
55161     this.lookup = {};
55162
55163     // if no id, create one
55164     // if the column does not have a dataIndex mapping,
55165     // map it to the order it is in the config
55166     for(var i = 0, len = config.length; i < len; i++){
55167         var c = config[i];
55168         if(typeof c.dataIndex == "undefined"){
55169             c.dataIndex = i;
55170         }
55171         if(typeof c.renderer == "string"){
55172             c.renderer = Roo.util.Format[c.renderer];
55173         }
55174         if(typeof c.id == "undefined"){
55175             c.id = Roo.id();
55176         }
55177         if(c.editor && c.editor.xtype){
55178             c.editor  = Roo.factory(c.editor, Roo.grid);
55179         }
55180         if(c.editor && c.editor.isFormField){
55181             c.editor = new Roo.grid.GridEditor(c.editor);
55182         }
55183         this.lookup[c.id] = c;
55184     }
55185
55186     /**
55187      * The width of columns which have no width specified (defaults to 100)
55188      * @type Number
55189      */
55190     this.defaultWidth = 100;
55191
55192     /**
55193      * Default sortable of columns which have no sortable specified (defaults to false)
55194      * @type Boolean
55195      */
55196     this.defaultSortable = false;
55197
55198     this.addEvents({
55199         /**
55200              * @event widthchange
55201              * Fires when the width of a column changes.
55202              * @param {ColumnModel} this
55203              * @param {Number} columnIndex The column index
55204              * @param {Number} newWidth The new width
55205              */
55206             "widthchange": true,
55207         /**
55208              * @event headerchange
55209              * Fires when the text of a header changes.
55210              * @param {ColumnModel} this
55211              * @param {Number} columnIndex The column index
55212              * @param {Number} newText The new header text
55213              */
55214             "headerchange": true,
55215         /**
55216              * @event hiddenchange
55217              * Fires when a column is hidden or "unhidden".
55218              * @param {ColumnModel} this
55219              * @param {Number} columnIndex The column index
55220              * @param {Boolean} hidden true if hidden, false otherwise
55221              */
55222             "hiddenchange": true,
55223             /**
55224          * @event columnmoved
55225          * Fires when a column is moved.
55226          * @param {ColumnModel} this
55227          * @param {Number} oldIndex
55228          * @param {Number} newIndex
55229          */
55230         "columnmoved" : true,
55231         /**
55232          * @event columlockchange
55233          * Fires when a column's locked state is changed
55234          * @param {ColumnModel} this
55235          * @param {Number} colIndex
55236          * @param {Boolean} locked true if locked
55237          */
55238         "columnlockchange" : true
55239     });
55240     Roo.grid.ColumnModel.superclass.constructor.call(this);
55241 };
55242 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55243     /**
55244      * @cfg {String} header The header text to display in the Grid view.
55245      */
55246     /**
55247      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55248      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55249      * specified, the column's index is used as an index into the Record's data Array.
55250      */
55251     /**
55252      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55253      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55254      */
55255     /**
55256      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55257      * Defaults to the value of the {@link #defaultSortable} property.
55258      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55259      */
55260     /**
55261      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55262      */
55263     /**
55264      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55265      */
55266     /**
55267      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55268      */
55269     /**
55270      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55271      */
55272     /**
55273      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55274      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55275      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55276      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55277      */
55278        /**
55279      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55280      */
55281     /**
55282      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55283      */
55284     /**
55285      * @cfg {String} cursor (Optional)
55286      */
55287     /**
55288      * @cfg {String} tooltip (Optional)
55289      */
55290     /**
55291      * Returns the id of the column at the specified index.
55292      * @param {Number} index The column index
55293      * @return {String} the id
55294      */
55295     getColumnId : function(index){
55296         return this.config[index].id;
55297     },
55298
55299     /**
55300      * Returns the column for a specified id.
55301      * @param {String} id The column id
55302      * @return {Object} the column
55303      */
55304     getColumnById : function(id){
55305         return this.lookup[id];
55306     },
55307
55308     
55309     /**
55310      * Returns the column for a specified dataIndex.
55311      * @param {String} dataIndex The column dataIndex
55312      * @return {Object|Boolean} the column or false if not found
55313      */
55314     getColumnByDataIndex: function(dataIndex){
55315         var index = this.findColumnIndex(dataIndex);
55316         return index > -1 ? this.config[index] : false;
55317     },
55318     
55319     /**
55320      * Returns the index for a specified column id.
55321      * @param {String} id The column id
55322      * @return {Number} the index, or -1 if not found
55323      */
55324     getIndexById : function(id){
55325         for(var i = 0, len = this.config.length; i < len; i++){
55326             if(this.config[i].id == id){
55327                 return i;
55328             }
55329         }
55330         return -1;
55331     },
55332     
55333     /**
55334      * Returns the index for a specified column dataIndex.
55335      * @param {String} dataIndex The column dataIndex
55336      * @return {Number} the index, or -1 if not found
55337      */
55338     
55339     findColumnIndex : function(dataIndex){
55340         for(var i = 0, len = this.config.length; i < len; i++){
55341             if(this.config[i].dataIndex == dataIndex){
55342                 return i;
55343             }
55344         }
55345         return -1;
55346     },
55347     
55348     
55349     moveColumn : function(oldIndex, newIndex){
55350         var c = this.config[oldIndex];
55351         this.config.splice(oldIndex, 1);
55352         this.config.splice(newIndex, 0, c);
55353         this.dataMap = null;
55354         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55355     },
55356
55357     isLocked : function(colIndex){
55358         return this.config[colIndex].locked === true;
55359     },
55360
55361     setLocked : function(colIndex, value, suppressEvent){
55362         if(this.isLocked(colIndex) == value){
55363             return;
55364         }
55365         this.config[colIndex].locked = value;
55366         if(!suppressEvent){
55367             this.fireEvent("columnlockchange", this, colIndex, value);
55368         }
55369     },
55370
55371     getTotalLockedWidth : function(){
55372         var totalWidth = 0;
55373         for(var i = 0; i < this.config.length; i++){
55374             if(this.isLocked(i) && !this.isHidden(i)){
55375                 this.totalWidth += this.getColumnWidth(i);
55376             }
55377         }
55378         return totalWidth;
55379     },
55380
55381     getLockedCount : function(){
55382         for(var i = 0, len = this.config.length; i < len; i++){
55383             if(!this.isLocked(i)){
55384                 return i;
55385             }
55386         }
55387     },
55388
55389     /**
55390      * Returns the number of columns.
55391      * @return {Number}
55392      */
55393     getColumnCount : function(visibleOnly){
55394         if(visibleOnly === true){
55395             var c = 0;
55396             for(var i = 0, len = this.config.length; i < len; i++){
55397                 if(!this.isHidden(i)){
55398                     c++;
55399                 }
55400             }
55401             return c;
55402         }
55403         return this.config.length;
55404     },
55405
55406     /**
55407      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55408      * @param {Function} fn
55409      * @param {Object} scope (optional)
55410      * @return {Array} result
55411      */
55412     getColumnsBy : function(fn, scope){
55413         var r = [];
55414         for(var i = 0, len = this.config.length; i < len; i++){
55415             var c = this.config[i];
55416             if(fn.call(scope||this, c, i) === true){
55417                 r[r.length] = c;
55418             }
55419         }
55420         return r;
55421     },
55422
55423     /**
55424      * Returns true if the specified column is sortable.
55425      * @param {Number} col The column index
55426      * @return {Boolean}
55427      */
55428     isSortable : function(col){
55429         if(typeof this.config[col].sortable == "undefined"){
55430             return this.defaultSortable;
55431         }
55432         return this.config[col].sortable;
55433     },
55434
55435     /**
55436      * Returns the rendering (formatting) function defined for the column.
55437      * @param {Number} col The column index.
55438      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55439      */
55440     getRenderer : function(col){
55441         if(!this.config[col].renderer){
55442             return Roo.grid.ColumnModel.defaultRenderer;
55443         }
55444         return this.config[col].renderer;
55445     },
55446
55447     /**
55448      * Sets the rendering (formatting) function for a column.
55449      * @param {Number} col The column index
55450      * @param {Function} fn The function to use to process the cell's raw data
55451      * to return HTML markup for the grid view. The render function is called with
55452      * the following parameters:<ul>
55453      * <li>Data value.</li>
55454      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55455      * <li>css A CSS style string to apply to the table cell.</li>
55456      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55457      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55458      * <li>Row index</li>
55459      * <li>Column index</li>
55460      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55461      */
55462     setRenderer : function(col, fn){
55463         this.config[col].renderer = fn;
55464     },
55465
55466     /**
55467      * Returns the width for the specified column.
55468      * @param {Number} col The column index
55469      * @return {Number}
55470      */
55471     getColumnWidth : function(col){
55472         return this.config[col].width * 1 || this.defaultWidth;
55473     },
55474
55475     /**
55476      * Sets the width for a column.
55477      * @param {Number} col The column index
55478      * @param {Number} width The new width
55479      */
55480     setColumnWidth : function(col, width, suppressEvent){
55481         this.config[col].width = width;
55482         this.totalWidth = null;
55483         if(!suppressEvent){
55484              this.fireEvent("widthchange", this, col, width);
55485         }
55486     },
55487
55488     /**
55489      * Returns the total width of all columns.
55490      * @param {Boolean} includeHidden True to include hidden column widths
55491      * @return {Number}
55492      */
55493     getTotalWidth : function(includeHidden){
55494         if(!this.totalWidth){
55495             this.totalWidth = 0;
55496             for(var i = 0, len = this.config.length; i < len; i++){
55497                 if(includeHidden || !this.isHidden(i)){
55498                     this.totalWidth += this.getColumnWidth(i);
55499                 }
55500             }
55501         }
55502         return this.totalWidth;
55503     },
55504
55505     /**
55506      * Returns the header for the specified column.
55507      * @param {Number} col The column index
55508      * @return {String}
55509      */
55510     getColumnHeader : function(col){
55511         return this.config[col].header;
55512     },
55513
55514     /**
55515      * Sets the header for a column.
55516      * @param {Number} col The column index
55517      * @param {String} header The new header
55518      */
55519     setColumnHeader : function(col, header){
55520         this.config[col].header = header;
55521         this.fireEvent("headerchange", this, col, header);
55522     },
55523
55524     /**
55525      * Returns the tooltip for the specified column.
55526      * @param {Number} col The column index
55527      * @return {String}
55528      */
55529     getColumnTooltip : function(col){
55530             return this.config[col].tooltip;
55531     },
55532     /**
55533      * Sets the tooltip for a column.
55534      * @param {Number} col The column index
55535      * @param {String} tooltip The new tooltip
55536      */
55537     setColumnTooltip : function(col, tooltip){
55538             this.config[col].tooltip = tooltip;
55539     },
55540
55541     /**
55542      * Returns the dataIndex for the specified column.
55543      * @param {Number} col The column index
55544      * @return {Number}
55545      */
55546     getDataIndex : function(col){
55547         return this.config[col].dataIndex;
55548     },
55549
55550     /**
55551      * Sets the dataIndex for a column.
55552      * @param {Number} col The column index
55553      * @param {Number} dataIndex The new dataIndex
55554      */
55555     setDataIndex : function(col, dataIndex){
55556         this.config[col].dataIndex = dataIndex;
55557     },
55558
55559     
55560     
55561     /**
55562      * Returns true if the cell is editable.
55563      * @param {Number} colIndex The column index
55564      * @param {Number} rowIndex The row index
55565      * @return {Boolean}
55566      */
55567     isCellEditable : function(colIndex, rowIndex){
55568         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55569     },
55570
55571     /**
55572      * Returns the editor defined for the cell/column.
55573      * return false or null to disable editing.
55574      * @param {Number} colIndex The column index
55575      * @param {Number} rowIndex The row index
55576      * @return {Object}
55577      */
55578     getCellEditor : function(colIndex, rowIndex){
55579         return this.config[colIndex].editor;
55580     },
55581
55582     /**
55583      * Sets if a column is editable.
55584      * @param {Number} col The column index
55585      * @param {Boolean} editable True if the column is editable
55586      */
55587     setEditable : function(col, editable){
55588         this.config[col].editable = editable;
55589     },
55590
55591
55592     /**
55593      * Returns true if the column is hidden.
55594      * @param {Number} colIndex The column index
55595      * @return {Boolean}
55596      */
55597     isHidden : function(colIndex){
55598         return this.config[colIndex].hidden;
55599     },
55600
55601
55602     /**
55603      * Returns true if the column width cannot be changed
55604      */
55605     isFixed : function(colIndex){
55606         return this.config[colIndex].fixed;
55607     },
55608
55609     /**
55610      * Returns true if the column can be resized
55611      * @return {Boolean}
55612      */
55613     isResizable : function(colIndex){
55614         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55615     },
55616     /**
55617      * Sets if a column is hidden.
55618      * @param {Number} colIndex The column index
55619      * @param {Boolean} hidden True if the column is hidden
55620      */
55621     setHidden : function(colIndex, hidden){
55622         this.config[colIndex].hidden = hidden;
55623         this.totalWidth = null;
55624         this.fireEvent("hiddenchange", this, colIndex, hidden);
55625     },
55626
55627     /**
55628      * Sets the editor for a column.
55629      * @param {Number} col The column index
55630      * @param {Object} editor The editor object
55631      */
55632     setEditor : function(col, editor){
55633         this.config[col].editor = editor;
55634     }
55635 });
55636
55637 Roo.grid.ColumnModel.defaultRenderer = function(value){
55638         if(typeof value == "string" && value.length < 1){
55639             return "&#160;";
55640         }
55641         return value;
55642 };
55643
55644 // Alias for backwards compatibility
55645 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55646 /*
55647  * Based on:
55648  * Ext JS Library 1.1.1
55649  * Copyright(c) 2006-2007, Ext JS, LLC.
55650  *
55651  * Originally Released Under LGPL - original licence link has changed is not relivant.
55652  *
55653  * Fork - LGPL
55654  * <script type="text/javascript">
55655  */
55656
55657 /**
55658  * @class Roo.grid.AbstractSelectionModel
55659  * @extends Roo.util.Observable
55660  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55661  * implemented by descendant classes.  This class should not be directly instantiated.
55662  * @constructor
55663  */
55664 Roo.grid.AbstractSelectionModel = function(){
55665     this.locked = false;
55666     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55667 };
55668
55669 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55670     /** @ignore Called by the grid automatically. Do not call directly. */
55671     init : function(grid){
55672         this.grid = grid;
55673         this.initEvents();
55674     },
55675
55676     /**
55677      * Locks the selections.
55678      */
55679     lock : function(){
55680         this.locked = true;
55681     },
55682
55683     /**
55684      * Unlocks the selections.
55685      */
55686     unlock : function(){
55687         this.locked = false;
55688     },
55689
55690     /**
55691      * Returns true if the selections are locked.
55692      * @return {Boolean}
55693      */
55694     isLocked : function(){
55695         return this.locked;
55696     }
55697 });/*
55698  * Based on:
55699  * Ext JS Library 1.1.1
55700  * Copyright(c) 2006-2007, Ext JS, LLC.
55701  *
55702  * Originally Released Under LGPL - original licence link has changed is not relivant.
55703  *
55704  * Fork - LGPL
55705  * <script type="text/javascript">
55706  */
55707 /**
55708  * @extends Roo.grid.AbstractSelectionModel
55709  * @class Roo.grid.RowSelectionModel
55710  * The default SelectionModel used by {@link Roo.grid.Grid}.
55711  * It supports multiple selections and keyboard selection/navigation. 
55712  * @constructor
55713  * @param {Object} config
55714  */
55715 Roo.grid.RowSelectionModel = function(config){
55716     Roo.apply(this, config);
55717     this.selections = new Roo.util.MixedCollection(false, function(o){
55718         return o.id;
55719     });
55720
55721     this.last = false;
55722     this.lastActive = false;
55723
55724     this.addEvents({
55725         /**
55726              * @event selectionchange
55727              * Fires when the selection changes
55728              * @param {SelectionModel} this
55729              */
55730             "selectionchange" : true,
55731         /**
55732              * @event afterselectionchange
55733              * Fires after the selection changes (eg. by key press or clicking)
55734              * @param {SelectionModel} this
55735              */
55736             "afterselectionchange" : true,
55737         /**
55738              * @event beforerowselect
55739              * Fires when a row is selected being selected, return false to cancel.
55740              * @param {SelectionModel} this
55741              * @param {Number} rowIndex The selected index
55742              * @param {Boolean} keepExisting False if other selections will be cleared
55743              */
55744             "beforerowselect" : true,
55745         /**
55746              * @event rowselect
55747              * Fires when a row is selected.
55748              * @param {SelectionModel} this
55749              * @param {Number} rowIndex The selected index
55750              * @param {Roo.data.Record} r The record
55751              */
55752             "rowselect" : true,
55753         /**
55754              * @event rowdeselect
55755              * Fires when a row is deselected.
55756              * @param {SelectionModel} this
55757              * @param {Number} rowIndex The selected index
55758              */
55759         "rowdeselect" : true
55760     });
55761     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55762     this.locked = false;
55763 };
55764
55765 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55766     /**
55767      * @cfg {Boolean} singleSelect
55768      * True to allow selection of only one row at a time (defaults to false)
55769      */
55770     singleSelect : false,
55771
55772     // private
55773     initEvents : function(){
55774
55775         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55776             this.grid.on("mousedown", this.handleMouseDown, this);
55777         }else{ // allow click to work like normal
55778             this.grid.on("rowclick", this.handleDragableRowClick, this);
55779         }
55780
55781         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55782             "up" : function(e){
55783                 if(!e.shiftKey){
55784                     this.selectPrevious(e.shiftKey);
55785                 }else if(this.last !== false && this.lastActive !== false){
55786                     var last = this.last;
55787                     this.selectRange(this.last,  this.lastActive-1);
55788                     this.grid.getView().focusRow(this.lastActive);
55789                     if(last !== false){
55790                         this.last = last;
55791                     }
55792                 }else{
55793                     this.selectFirstRow();
55794                 }
55795                 this.fireEvent("afterselectionchange", this);
55796             },
55797             "down" : function(e){
55798                 if(!e.shiftKey){
55799                     this.selectNext(e.shiftKey);
55800                 }else if(this.last !== false && this.lastActive !== false){
55801                     var last = this.last;
55802                     this.selectRange(this.last,  this.lastActive+1);
55803                     this.grid.getView().focusRow(this.lastActive);
55804                     if(last !== false){
55805                         this.last = last;
55806                     }
55807                 }else{
55808                     this.selectFirstRow();
55809                 }
55810                 this.fireEvent("afterselectionchange", this);
55811             },
55812             scope: this
55813         });
55814
55815         var view = this.grid.view;
55816         view.on("refresh", this.onRefresh, this);
55817         view.on("rowupdated", this.onRowUpdated, this);
55818         view.on("rowremoved", this.onRemove, this);
55819     },
55820
55821     // private
55822     onRefresh : function(){
55823         var ds = this.grid.dataSource, i, v = this.grid.view;
55824         var s = this.selections;
55825         s.each(function(r){
55826             if((i = ds.indexOfId(r.id)) != -1){
55827                 v.onRowSelect(i);
55828             }else{
55829                 s.remove(r);
55830             }
55831         });
55832     },
55833
55834     // private
55835     onRemove : function(v, index, r){
55836         this.selections.remove(r);
55837     },
55838
55839     // private
55840     onRowUpdated : function(v, index, r){
55841         if(this.isSelected(r)){
55842             v.onRowSelect(index);
55843         }
55844     },
55845
55846     /**
55847      * Select records.
55848      * @param {Array} records The records to select
55849      * @param {Boolean} keepExisting (optional) True to keep existing selections
55850      */
55851     selectRecords : function(records, keepExisting){
55852         if(!keepExisting){
55853             this.clearSelections();
55854         }
55855         var ds = this.grid.dataSource;
55856         for(var i = 0, len = records.length; i < len; i++){
55857             this.selectRow(ds.indexOf(records[i]), true);
55858         }
55859     },
55860
55861     /**
55862      * Gets the number of selected rows.
55863      * @return {Number}
55864      */
55865     getCount : function(){
55866         return this.selections.length;
55867     },
55868
55869     /**
55870      * Selects the first row in the grid.
55871      */
55872     selectFirstRow : function(){
55873         this.selectRow(0);
55874     },
55875
55876     /**
55877      * Select the last row.
55878      * @param {Boolean} keepExisting (optional) True to keep existing selections
55879      */
55880     selectLastRow : function(keepExisting){
55881         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55882     },
55883
55884     /**
55885      * Selects the row immediately following the last selected row.
55886      * @param {Boolean} keepExisting (optional) True to keep existing selections
55887      */
55888     selectNext : function(keepExisting){
55889         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55890             this.selectRow(this.last+1, keepExisting);
55891             this.grid.getView().focusRow(this.last);
55892         }
55893     },
55894
55895     /**
55896      * Selects the row that precedes the last selected row.
55897      * @param {Boolean} keepExisting (optional) True to keep existing selections
55898      */
55899     selectPrevious : function(keepExisting){
55900         if(this.last){
55901             this.selectRow(this.last-1, keepExisting);
55902             this.grid.getView().focusRow(this.last);
55903         }
55904     },
55905
55906     /**
55907      * Returns the selected records
55908      * @return {Array} Array of selected records
55909      */
55910     getSelections : function(){
55911         return [].concat(this.selections.items);
55912     },
55913
55914     /**
55915      * Returns the first selected record.
55916      * @return {Record}
55917      */
55918     getSelected : function(){
55919         return this.selections.itemAt(0);
55920     },
55921
55922
55923     /**
55924      * Clears all selections.
55925      */
55926     clearSelections : function(fast){
55927         if(this.locked) return;
55928         if(fast !== true){
55929             var ds = this.grid.dataSource;
55930             var s = this.selections;
55931             s.each(function(r){
55932                 this.deselectRow(ds.indexOfId(r.id));
55933             }, this);
55934             s.clear();
55935         }else{
55936             this.selections.clear();
55937         }
55938         this.last = false;
55939     },
55940
55941
55942     /**
55943      * Selects all rows.
55944      */
55945     selectAll : function(){
55946         if(this.locked) return;
55947         this.selections.clear();
55948         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55949             this.selectRow(i, true);
55950         }
55951     },
55952
55953     /**
55954      * Returns True if there is a selection.
55955      * @return {Boolean}
55956      */
55957     hasSelection : function(){
55958         return this.selections.length > 0;
55959     },
55960
55961     /**
55962      * Returns True if the specified row is selected.
55963      * @param {Number/Record} record The record or index of the record to check
55964      * @return {Boolean}
55965      */
55966     isSelected : function(index){
55967         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55968         return (r && this.selections.key(r.id) ? true : false);
55969     },
55970
55971     /**
55972      * Returns True if the specified record id is selected.
55973      * @param {String} id The id of record to check
55974      * @return {Boolean}
55975      */
55976     isIdSelected : function(id){
55977         return (this.selections.key(id) ? true : false);
55978     },
55979
55980     // private
55981     handleMouseDown : function(e, t){
55982         var view = this.grid.getView(), rowIndex;
55983         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55984             return;
55985         };
55986         if(e.shiftKey && this.last !== false){
55987             var last = this.last;
55988             this.selectRange(last, rowIndex, e.ctrlKey);
55989             this.last = last; // reset the last
55990             view.focusRow(rowIndex);
55991         }else{
55992             var isSelected = this.isSelected(rowIndex);
55993             if(e.button !== 0 && isSelected){
55994                 view.focusRow(rowIndex);
55995             }else if(e.ctrlKey && isSelected){
55996                 this.deselectRow(rowIndex);
55997             }else if(!isSelected){
55998                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55999                 view.focusRow(rowIndex);
56000             }
56001         }
56002         this.fireEvent("afterselectionchange", this);
56003     },
56004     // private
56005     handleDragableRowClick :  function(grid, rowIndex, e) 
56006     {
56007         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56008             this.selectRow(rowIndex, false);
56009             grid.view.focusRow(rowIndex);
56010              this.fireEvent("afterselectionchange", this);
56011         }
56012     },
56013     
56014     /**
56015      * Selects multiple rows.
56016      * @param {Array} rows Array of the indexes of the row to select
56017      * @param {Boolean} keepExisting (optional) True to keep existing selections
56018      */
56019     selectRows : function(rows, keepExisting){
56020         if(!keepExisting){
56021             this.clearSelections();
56022         }
56023         for(var i = 0, len = rows.length; i < len; i++){
56024             this.selectRow(rows[i], true);
56025         }
56026     },
56027
56028     /**
56029      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56030      * @param {Number} startRow The index of the first row in the range
56031      * @param {Number} endRow The index of the last row in the range
56032      * @param {Boolean} keepExisting (optional) True to retain existing selections
56033      */
56034     selectRange : function(startRow, endRow, keepExisting){
56035         if(this.locked) return;
56036         if(!keepExisting){
56037             this.clearSelections();
56038         }
56039         if(startRow <= endRow){
56040             for(var i = startRow; i <= endRow; i++){
56041                 this.selectRow(i, true);
56042             }
56043         }else{
56044             for(var i = startRow; i >= endRow; i--){
56045                 this.selectRow(i, true);
56046             }
56047         }
56048     },
56049
56050     /**
56051      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56052      * @param {Number} startRow The index of the first row in the range
56053      * @param {Number} endRow The index of the last row in the range
56054      */
56055     deselectRange : function(startRow, endRow, preventViewNotify){
56056         if(this.locked) return;
56057         for(var i = startRow; i <= endRow; i++){
56058             this.deselectRow(i, preventViewNotify);
56059         }
56060     },
56061
56062     /**
56063      * Selects a row.
56064      * @param {Number} row The index of the row to select
56065      * @param {Boolean} keepExisting (optional) True to keep existing selections
56066      */
56067     selectRow : function(index, keepExisting, preventViewNotify){
56068         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56069         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56070             if(!keepExisting || this.singleSelect){
56071                 this.clearSelections();
56072             }
56073             var r = this.grid.dataSource.getAt(index);
56074             this.selections.add(r);
56075             this.last = this.lastActive = index;
56076             if(!preventViewNotify){
56077                 this.grid.getView().onRowSelect(index);
56078             }
56079             this.fireEvent("rowselect", this, index, r);
56080             this.fireEvent("selectionchange", this);
56081         }
56082     },
56083
56084     /**
56085      * Deselects a row.
56086      * @param {Number} row The index of the row to deselect
56087      */
56088     deselectRow : function(index, preventViewNotify){
56089         if(this.locked) return;
56090         if(this.last == index){
56091             this.last = false;
56092         }
56093         if(this.lastActive == index){
56094             this.lastActive = false;
56095         }
56096         var r = this.grid.dataSource.getAt(index);
56097         this.selections.remove(r);
56098         if(!preventViewNotify){
56099             this.grid.getView().onRowDeselect(index);
56100         }
56101         this.fireEvent("rowdeselect", this, index);
56102         this.fireEvent("selectionchange", this);
56103     },
56104
56105     // private
56106     restoreLast : function(){
56107         if(this._last){
56108             this.last = this._last;
56109         }
56110     },
56111
56112     // private
56113     acceptsNav : function(row, col, cm){
56114         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56115     },
56116
56117     // private
56118     onEditorKey : function(field, e){
56119         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56120         if(k == e.TAB){
56121             e.stopEvent();
56122             ed.completeEdit();
56123             if(e.shiftKey){
56124                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56125             }else{
56126                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56127             }
56128         }else if(k == e.ENTER && !e.ctrlKey){
56129             e.stopEvent();
56130             ed.completeEdit();
56131             if(e.shiftKey){
56132                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56133             }else{
56134                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56135             }
56136         }else if(k == e.ESC){
56137             ed.cancelEdit();
56138         }
56139         if(newCell){
56140             g.startEditing(newCell[0], newCell[1]);
56141         }
56142     }
56143 });/*
56144  * Based on:
56145  * Ext JS Library 1.1.1
56146  * Copyright(c) 2006-2007, Ext JS, LLC.
56147  *
56148  * Originally Released Under LGPL - original licence link has changed is not relivant.
56149  *
56150  * Fork - LGPL
56151  * <script type="text/javascript">
56152  */
56153 /**
56154  * @class Roo.grid.CellSelectionModel
56155  * @extends Roo.grid.AbstractSelectionModel
56156  * This class provides the basic implementation for cell selection in a grid.
56157  * @constructor
56158  * @param {Object} config The object containing the configuration of this model.
56159  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56160  */
56161 Roo.grid.CellSelectionModel = function(config){
56162     Roo.apply(this, config);
56163
56164     this.selection = null;
56165
56166     this.addEvents({
56167         /**
56168              * @event beforerowselect
56169              * Fires before a cell is selected.
56170              * @param {SelectionModel} this
56171              * @param {Number} rowIndex The selected row index
56172              * @param {Number} colIndex The selected cell index
56173              */
56174             "beforecellselect" : true,
56175         /**
56176              * @event cellselect
56177              * Fires when a cell is selected.
56178              * @param {SelectionModel} this
56179              * @param {Number} rowIndex The selected row index
56180              * @param {Number} colIndex The selected cell index
56181              */
56182             "cellselect" : true,
56183         /**
56184              * @event selectionchange
56185              * Fires when the active selection changes.
56186              * @param {SelectionModel} this
56187              * @param {Object} selection null for no selection or an object (o) with two properties
56188                 <ul>
56189                 <li>o.record: the record object for the row the selection is in</li>
56190                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56191                 </ul>
56192              */
56193             "selectionchange" : true,
56194         /**
56195              * @event tabend
56196              * Fires when the tab (or enter) was pressed on the last editable cell
56197              * You can use this to trigger add new row.
56198              * @param {SelectionModel} this
56199              */
56200             "tabend" : true,
56201          /**
56202              * @event beforeeditnext
56203              * Fires before the next editable sell is made active
56204              * You can use this to skip to another cell or fire the tabend
56205              *    if you set cell to false
56206              * @param {Object} eventdata object : { cell : [ row, col ] } 
56207              */
56208             "beforeeditnext" : true
56209     });
56210     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56211 };
56212
56213 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56214     
56215     enter_is_tab: false,
56216
56217     /** @ignore */
56218     initEvents : function(){
56219         this.grid.on("mousedown", this.handleMouseDown, this);
56220         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56221         var view = this.grid.view;
56222         view.on("refresh", this.onViewChange, this);
56223         view.on("rowupdated", this.onRowUpdated, this);
56224         view.on("beforerowremoved", this.clearSelections, this);
56225         view.on("beforerowsinserted", this.clearSelections, this);
56226         if(this.grid.isEditor){
56227             this.grid.on("beforeedit", this.beforeEdit,  this);
56228         }
56229     },
56230
56231         //private
56232     beforeEdit : function(e){
56233         this.select(e.row, e.column, false, true, e.record);
56234     },
56235
56236         //private
56237     onRowUpdated : function(v, index, r){
56238         if(this.selection && this.selection.record == r){
56239             v.onCellSelect(index, this.selection.cell[1]);
56240         }
56241     },
56242
56243         //private
56244     onViewChange : function(){
56245         this.clearSelections(true);
56246     },
56247
56248         /**
56249          * Returns the currently selected cell,.
56250          * @return {Array} The selected cell (row, column) or null if none selected.
56251          */
56252     getSelectedCell : function(){
56253         return this.selection ? this.selection.cell : null;
56254     },
56255
56256     /**
56257      * Clears all selections.
56258      * @param {Boolean} true to prevent the gridview from being notified about the change.
56259      */
56260     clearSelections : function(preventNotify){
56261         var s = this.selection;
56262         if(s){
56263             if(preventNotify !== true){
56264                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56265             }
56266             this.selection = null;
56267             this.fireEvent("selectionchange", this, null);
56268         }
56269     },
56270
56271     /**
56272      * Returns true if there is a selection.
56273      * @return {Boolean}
56274      */
56275     hasSelection : function(){
56276         return this.selection ? true : false;
56277     },
56278
56279     /** @ignore */
56280     handleMouseDown : function(e, t){
56281         var v = this.grid.getView();
56282         if(this.isLocked()){
56283             return;
56284         };
56285         var row = v.findRowIndex(t);
56286         var cell = v.findCellIndex(t);
56287         if(row !== false && cell !== false){
56288             this.select(row, cell);
56289         }
56290     },
56291
56292     /**
56293      * Selects a cell.
56294      * @param {Number} rowIndex
56295      * @param {Number} collIndex
56296      */
56297     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56298         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56299             this.clearSelections();
56300             r = r || this.grid.dataSource.getAt(rowIndex);
56301             this.selection = {
56302                 record : r,
56303                 cell : [rowIndex, colIndex]
56304             };
56305             if(!preventViewNotify){
56306                 var v = this.grid.getView();
56307                 v.onCellSelect(rowIndex, colIndex);
56308                 if(preventFocus !== true){
56309                     v.focusCell(rowIndex, colIndex);
56310                 }
56311             }
56312             this.fireEvent("cellselect", this, rowIndex, colIndex);
56313             this.fireEvent("selectionchange", this, this.selection);
56314         }
56315     },
56316
56317         //private
56318     isSelectable : function(rowIndex, colIndex, cm){
56319         return !cm.isHidden(colIndex);
56320     },
56321
56322     /** @ignore */
56323     handleKeyDown : function(e){
56324         //Roo.log('Cell Sel Model handleKeyDown');
56325         if(!e.isNavKeyPress()){
56326             return;
56327         }
56328         var g = this.grid, s = this.selection;
56329         if(!s){
56330             e.stopEvent();
56331             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56332             if(cell){
56333                 this.select(cell[0], cell[1]);
56334             }
56335             return;
56336         }
56337         var sm = this;
56338         var walk = function(row, col, step){
56339             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56340         };
56341         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56342         var newCell;
56343
56344       
56345
56346         switch(k){
56347             case e.TAB:
56348                 // handled by onEditorKey
56349                 if (g.isEditor && g.editing) {
56350                     return;
56351                 }
56352                 if(e.shiftKey) {
56353                     newCell = walk(r, c-1, -1);
56354                 } else {
56355                     newCell = walk(r, c+1, 1);
56356                 }
56357                 break;
56358             
56359             case e.DOWN:
56360                newCell = walk(r+1, c, 1);
56361                 break;
56362             
56363             case e.UP:
56364                 newCell = walk(r-1, c, -1);
56365                 break;
56366             
56367             case e.RIGHT:
56368                 newCell = walk(r, c+1, 1);
56369                 break;
56370             
56371             case e.LEFT:
56372                 newCell = walk(r, c-1, -1);
56373                 break;
56374             
56375             case e.ENTER:
56376                 
56377                 if(g.isEditor && !g.editing){
56378                    g.startEditing(r, c);
56379                    e.stopEvent();
56380                    return;
56381                 }
56382                 
56383                 
56384              break;
56385         };
56386         if(newCell){
56387             this.select(newCell[0], newCell[1]);
56388             e.stopEvent();
56389             
56390         }
56391     },
56392
56393     acceptsNav : function(row, col, cm){
56394         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56395     },
56396     /**
56397      * Selects a cell.
56398      * @param {Number} field (not used) - as it's normally used as a listener
56399      * @param {Number} e - event - fake it by using
56400      *
56401      * var e = Roo.EventObjectImpl.prototype;
56402      * e.keyCode = e.TAB
56403      *
56404      * 
56405      */
56406     onEditorKey : function(field, e){
56407         
56408         var k = e.getKey(),
56409             newCell,
56410             g = this.grid,
56411             ed = g.activeEditor,
56412             forward = false;
56413         ///Roo.log('onEditorKey' + k);
56414         
56415         
56416         if (this.enter_is_tab && k == e.ENTER) {
56417             k = e.TAB;
56418         }
56419         
56420         if(k == e.TAB){
56421             if(e.shiftKey){
56422                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56423             }else{
56424                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56425                 forward = true;
56426             }
56427             
56428             e.stopEvent();
56429             
56430         } else if(k == e.ENTER &&  !e.ctrlKey){
56431             ed.completeEdit();
56432             e.stopEvent();
56433             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56434         
56435                 } else if(k == e.ESC){
56436             ed.cancelEdit();
56437         }
56438                 
56439         if (newCell) {
56440             var ecall = { cell : newCell, forward : forward };
56441             this.fireEvent('beforeeditnext', ecall );
56442             newCell = ecall.cell;
56443                         forward = ecall.forward;
56444         }
56445                 
56446         if(newCell){
56447             //Roo.log('next cell after edit');
56448             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56449         } else if (forward) {
56450             // tabbed past last
56451             this.fireEvent.defer(100, this, ['tabend',this]);
56452         }
56453     }
56454 });/*
56455  * Based on:
56456  * Ext JS Library 1.1.1
56457  * Copyright(c) 2006-2007, Ext JS, LLC.
56458  *
56459  * Originally Released Under LGPL - original licence link has changed is not relivant.
56460  *
56461  * Fork - LGPL
56462  * <script type="text/javascript">
56463  */
56464  
56465 /**
56466  * @class Roo.grid.EditorGrid
56467  * @extends Roo.grid.Grid
56468  * Class for creating and editable grid.
56469  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56470  * The container MUST have some type of size defined for the grid to fill. The container will be 
56471  * automatically set to position relative if it isn't already.
56472  * @param {Object} dataSource The data model to bind to
56473  * @param {Object} colModel The column model with info about this grid's columns
56474  */
56475 Roo.grid.EditorGrid = function(container, config){
56476     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56477     this.getGridEl().addClass("xedit-grid");
56478
56479     if(!this.selModel){
56480         this.selModel = new Roo.grid.CellSelectionModel();
56481     }
56482
56483     this.activeEditor = null;
56484
56485         this.addEvents({
56486             /**
56487              * @event beforeedit
56488              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56489              * <ul style="padding:5px;padding-left:16px;">
56490              * <li>grid - This grid</li>
56491              * <li>record - The record being edited</li>
56492              * <li>field - The field name being edited</li>
56493              * <li>value - The value for the field being edited.</li>
56494              * <li>row - The grid row index</li>
56495              * <li>column - The grid column index</li>
56496              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56497              * </ul>
56498              * @param {Object} e An edit event (see above for description)
56499              */
56500             "beforeedit" : true,
56501             /**
56502              * @event afteredit
56503              * Fires after a cell is edited. <br />
56504              * <ul style="padding:5px;padding-left:16px;">
56505              * <li>grid - This grid</li>
56506              * <li>record - The record being edited</li>
56507              * <li>field - The field name being edited</li>
56508              * <li>value - The value being set</li>
56509              * <li>originalValue - The original value for the field, before the edit.</li>
56510              * <li>row - The grid row index</li>
56511              * <li>column - The grid column index</li>
56512              * </ul>
56513              * @param {Object} e An edit event (see above for description)
56514              */
56515             "afteredit" : true,
56516             /**
56517              * @event validateedit
56518              * Fires after a cell is edited, but before the value is set in the record. 
56519          * You can use this to modify the value being set in the field, Return false
56520              * to cancel the change. The edit event object has the following properties <br />
56521              * <ul style="padding:5px;padding-left:16px;">
56522          * <li>editor - This editor</li>
56523              * <li>grid - This grid</li>
56524              * <li>record - The record being edited</li>
56525              * <li>field - The field name being edited</li>
56526              * <li>value - The value being set</li>
56527              * <li>originalValue - The original value for the field, before the edit.</li>
56528              * <li>row - The grid row index</li>
56529              * <li>column - The grid column index</li>
56530              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56531              * </ul>
56532              * @param {Object} e An edit event (see above for description)
56533              */
56534             "validateedit" : true
56535         });
56536     this.on("bodyscroll", this.stopEditing,  this);
56537     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56538 };
56539
56540 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56541     /**
56542      * @cfg {Number} clicksToEdit
56543      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56544      */
56545     clicksToEdit: 2,
56546
56547     // private
56548     isEditor : true,
56549     // private
56550     trackMouseOver: false, // causes very odd FF errors
56551
56552     onCellDblClick : function(g, row, col){
56553         this.startEditing(row, col);
56554     },
56555
56556     onEditComplete : function(ed, value, startValue){
56557         this.editing = false;
56558         this.activeEditor = null;
56559         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56560         var r = ed.record;
56561         var field = this.colModel.getDataIndex(ed.col);
56562         var e = {
56563             grid: this,
56564             record: r,
56565             field: field,
56566             originalValue: startValue,
56567             value: value,
56568             row: ed.row,
56569             column: ed.col,
56570             cancel:false,
56571             editor: ed
56572         };
56573         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56574         cell.show();
56575           
56576         if(String(value) !== String(startValue)){
56577             
56578             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56579                 r.set(field, e.value);
56580                 // if we are dealing with a combo box..
56581                 // then we also set the 'name' colum to be the displayField
56582                 if (ed.field.displayField && ed.field.name) {
56583                     r.set(ed.field.name, ed.field.el.dom.value);
56584                 }
56585                 
56586                 delete e.cancel; //?? why!!!
56587                 this.fireEvent("afteredit", e);
56588             }
56589         } else {
56590             this.fireEvent("afteredit", e); // always fire it!
56591         }
56592         this.view.focusCell(ed.row, ed.col);
56593     },
56594
56595     /**
56596      * Starts editing the specified for the specified row/column
56597      * @param {Number} rowIndex
56598      * @param {Number} colIndex
56599      */
56600     startEditing : function(row, col){
56601         this.stopEditing();
56602         if(this.colModel.isCellEditable(col, row)){
56603             this.view.ensureVisible(row, col, true);
56604           
56605             var r = this.dataSource.getAt(row);
56606             var field = this.colModel.getDataIndex(col);
56607             var cell = Roo.get(this.view.getCell(row,col));
56608             var e = {
56609                 grid: this,
56610                 record: r,
56611                 field: field,
56612                 value: r.data[field],
56613                 row: row,
56614                 column: col,
56615                 cancel:false 
56616             };
56617             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56618                 this.editing = true;
56619                 var ed = this.colModel.getCellEditor(col, row);
56620                 
56621                 if (!ed) {
56622                     return;
56623                 }
56624                 if(!ed.rendered){
56625                     ed.render(ed.parentEl || document.body);
56626                 }
56627                 ed.field.reset();
56628                
56629                 cell.hide();
56630                 
56631                 (function(){ // complex but required for focus issues in safari, ie and opera
56632                     ed.row = row;
56633                     ed.col = col;
56634                     ed.record = r;
56635                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56636                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56637                     this.activeEditor = ed;
56638                     var v = r.data[field];
56639                     ed.startEdit(this.view.getCell(row, col), v);
56640                     // combo's with 'displayField and name set
56641                     if (ed.field.displayField && ed.field.name) {
56642                         ed.field.el.dom.value = r.data[ed.field.name];
56643                     }
56644                     
56645                     
56646                 }).defer(50, this);
56647             }
56648         }
56649     },
56650         
56651     /**
56652      * Stops any active editing
56653      */
56654     stopEditing : function(){
56655         if(this.activeEditor){
56656             this.activeEditor.completeEdit();
56657         }
56658         this.activeEditor = null;
56659     },
56660         
56661          /**
56662      * Called to get grid's drag proxy text, by default returns this.ddText.
56663      * @return {String}
56664      */
56665     getDragDropText : function(){
56666         var count = this.selModel.getSelectedCell() ? 1 : 0;
56667         return String.format(this.ddText, count, count == 1 ? '' : 's');
56668     }
56669         
56670 });/*
56671  * Based on:
56672  * Ext JS Library 1.1.1
56673  * Copyright(c) 2006-2007, Ext JS, LLC.
56674  *
56675  * Originally Released Under LGPL - original licence link has changed is not relivant.
56676  *
56677  * Fork - LGPL
56678  * <script type="text/javascript">
56679  */
56680
56681 // private - not really -- you end up using it !
56682 // This is a support class used internally by the Grid components
56683
56684 /**
56685  * @class Roo.grid.GridEditor
56686  * @extends Roo.Editor
56687  * Class for creating and editable grid elements.
56688  * @param {Object} config any settings (must include field)
56689  */
56690 Roo.grid.GridEditor = function(field, config){
56691     if (!config && field.field) {
56692         config = field;
56693         field = Roo.factory(config.field, Roo.form);
56694     }
56695     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56696     field.monitorTab = false;
56697 };
56698
56699 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56700     
56701     /**
56702      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56703      */
56704     
56705     alignment: "tl-tl",
56706     autoSize: "width",
56707     hideEl : false,
56708     cls: "x-small-editor x-grid-editor",
56709     shim:false,
56710     shadow:"frame"
56711 });/*
56712  * Based on:
56713  * Ext JS Library 1.1.1
56714  * Copyright(c) 2006-2007, Ext JS, LLC.
56715  *
56716  * Originally Released Under LGPL - original licence link has changed is not relivant.
56717  *
56718  * Fork - LGPL
56719  * <script type="text/javascript">
56720  */
56721   
56722
56723   
56724 Roo.grid.PropertyRecord = Roo.data.Record.create([
56725     {name:'name',type:'string'},  'value'
56726 ]);
56727
56728
56729 Roo.grid.PropertyStore = function(grid, source){
56730     this.grid = grid;
56731     this.store = new Roo.data.Store({
56732         recordType : Roo.grid.PropertyRecord
56733     });
56734     this.store.on('update', this.onUpdate,  this);
56735     if(source){
56736         this.setSource(source);
56737     }
56738     Roo.grid.PropertyStore.superclass.constructor.call(this);
56739 };
56740
56741
56742
56743 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56744     setSource : function(o){
56745         this.source = o;
56746         this.store.removeAll();
56747         var data = [];
56748         for(var k in o){
56749             if(this.isEditableValue(o[k])){
56750                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56751             }
56752         }
56753         this.store.loadRecords({records: data}, {}, true);
56754     },
56755
56756     onUpdate : function(ds, record, type){
56757         if(type == Roo.data.Record.EDIT){
56758             var v = record.data['value'];
56759             var oldValue = record.modified['value'];
56760             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56761                 this.source[record.id] = v;
56762                 record.commit();
56763                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56764             }else{
56765                 record.reject();
56766             }
56767         }
56768     },
56769
56770     getProperty : function(row){
56771        return this.store.getAt(row);
56772     },
56773
56774     isEditableValue: function(val){
56775         if(val && val instanceof Date){
56776             return true;
56777         }else if(typeof val == 'object' || typeof val == 'function'){
56778             return false;
56779         }
56780         return true;
56781     },
56782
56783     setValue : function(prop, value){
56784         this.source[prop] = value;
56785         this.store.getById(prop).set('value', value);
56786     },
56787
56788     getSource : function(){
56789         return this.source;
56790     }
56791 });
56792
56793 Roo.grid.PropertyColumnModel = function(grid, store){
56794     this.grid = grid;
56795     var g = Roo.grid;
56796     g.PropertyColumnModel.superclass.constructor.call(this, [
56797         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56798         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56799     ]);
56800     this.store = store;
56801     this.bselect = Roo.DomHelper.append(document.body, {
56802         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56803             {tag: 'option', value: 'true', html: 'true'},
56804             {tag: 'option', value: 'false', html: 'false'}
56805         ]
56806     });
56807     Roo.id(this.bselect);
56808     var f = Roo.form;
56809     this.editors = {
56810         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56811         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56812         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56813         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56814         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56815     };
56816     this.renderCellDelegate = this.renderCell.createDelegate(this);
56817     this.renderPropDelegate = this.renderProp.createDelegate(this);
56818 };
56819
56820 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56821     
56822     
56823     nameText : 'Name',
56824     valueText : 'Value',
56825     
56826     dateFormat : 'm/j/Y',
56827     
56828     
56829     renderDate : function(dateVal){
56830         return dateVal.dateFormat(this.dateFormat);
56831     },
56832
56833     renderBool : function(bVal){
56834         return bVal ? 'true' : 'false';
56835     },
56836
56837     isCellEditable : function(colIndex, rowIndex){
56838         return colIndex == 1;
56839     },
56840
56841     getRenderer : function(col){
56842         return col == 1 ?
56843             this.renderCellDelegate : this.renderPropDelegate;
56844     },
56845
56846     renderProp : function(v){
56847         return this.getPropertyName(v);
56848     },
56849
56850     renderCell : function(val){
56851         var rv = val;
56852         if(val instanceof Date){
56853             rv = this.renderDate(val);
56854         }else if(typeof val == 'boolean'){
56855             rv = this.renderBool(val);
56856         }
56857         return Roo.util.Format.htmlEncode(rv);
56858     },
56859
56860     getPropertyName : function(name){
56861         var pn = this.grid.propertyNames;
56862         return pn && pn[name] ? pn[name] : name;
56863     },
56864
56865     getCellEditor : function(colIndex, rowIndex){
56866         var p = this.store.getProperty(rowIndex);
56867         var n = p.data['name'], val = p.data['value'];
56868         
56869         if(typeof(this.grid.customEditors[n]) == 'string'){
56870             return this.editors[this.grid.customEditors[n]];
56871         }
56872         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56873             return this.grid.customEditors[n];
56874         }
56875         if(val instanceof Date){
56876             return this.editors['date'];
56877         }else if(typeof val == 'number'){
56878             return this.editors['number'];
56879         }else if(typeof val == 'boolean'){
56880             return this.editors['boolean'];
56881         }else{
56882             return this.editors['string'];
56883         }
56884     }
56885 });
56886
56887 /**
56888  * @class Roo.grid.PropertyGrid
56889  * @extends Roo.grid.EditorGrid
56890  * This class represents the  interface of a component based property grid control.
56891  * <br><br>Usage:<pre><code>
56892  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56893       
56894  });
56895  // set any options
56896  grid.render();
56897  * </code></pre>
56898   
56899  * @constructor
56900  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56901  * The container MUST have some type of size defined for the grid to fill. The container will be
56902  * automatically set to position relative if it isn't already.
56903  * @param {Object} config A config object that sets properties on this grid.
56904  */
56905 Roo.grid.PropertyGrid = function(container, config){
56906     config = config || {};
56907     var store = new Roo.grid.PropertyStore(this);
56908     this.store = store;
56909     var cm = new Roo.grid.PropertyColumnModel(this, store);
56910     store.store.sort('name', 'ASC');
56911     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56912         ds: store.store,
56913         cm: cm,
56914         enableColLock:false,
56915         enableColumnMove:false,
56916         stripeRows:false,
56917         trackMouseOver: false,
56918         clicksToEdit:1
56919     }, config));
56920     this.getGridEl().addClass('x-props-grid');
56921     this.lastEditRow = null;
56922     this.on('columnresize', this.onColumnResize, this);
56923     this.addEvents({
56924          /**
56925              * @event beforepropertychange
56926              * Fires before a property changes (return false to stop?)
56927              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56928              * @param {String} id Record Id
56929              * @param {String} newval New Value
56930          * @param {String} oldval Old Value
56931              */
56932         "beforepropertychange": true,
56933         /**
56934              * @event propertychange
56935              * Fires after a property changes
56936              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56937              * @param {String} id Record Id
56938              * @param {String} newval New Value
56939          * @param {String} oldval Old Value
56940              */
56941         "propertychange": true
56942     });
56943     this.customEditors = this.customEditors || {};
56944 };
56945 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56946     
56947      /**
56948      * @cfg {Object} customEditors map of colnames=> custom editors.
56949      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56950      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56951      * false disables editing of the field.
56952          */
56953     
56954       /**
56955      * @cfg {Object} propertyNames map of property Names to their displayed value
56956          */
56957     
56958     render : function(){
56959         Roo.grid.PropertyGrid.superclass.render.call(this);
56960         this.autoSize.defer(100, this);
56961     },
56962
56963     autoSize : function(){
56964         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56965         if(this.view){
56966             this.view.fitColumns();
56967         }
56968     },
56969
56970     onColumnResize : function(){
56971         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56972         this.autoSize();
56973     },
56974     /**
56975      * Sets the data for the Grid
56976      * accepts a Key => Value object of all the elements avaiable.
56977      * @param {Object} data  to appear in grid.
56978      */
56979     setSource : function(source){
56980         this.store.setSource(source);
56981         //this.autoSize();
56982     },
56983     /**
56984      * Gets all the data from the grid.
56985      * @return {Object} data  data stored in grid
56986      */
56987     getSource : function(){
56988         return this.store.getSource();
56989     }
56990 });/*
56991   
56992  * Licence LGPL
56993  
56994  */
56995  
56996 /**
56997  * @class Roo.grid.Calendar
56998  * @extends Roo.util.Grid
56999  * This class extends the Grid to provide a calendar widget
57000  * <br><br>Usage:<pre><code>
57001  var grid = new Roo.grid.Calendar("my-container-id", {
57002      ds: myDataStore,
57003      cm: myColModel,
57004      selModel: mySelectionModel,
57005      autoSizeColumns: true,
57006      monitorWindowResize: false,
57007      trackMouseOver: true
57008      eventstore : real data store..
57009  });
57010  // set any options
57011  grid.render();
57012   
57013   * @constructor
57014  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57015  * The container MUST have some type of size defined for the grid to fill. The container will be
57016  * automatically set to position relative if it isn't already.
57017  * @param {Object} config A config object that sets properties on this grid.
57018  */
57019 Roo.grid.Calendar = function(container, config){
57020         // initialize the container
57021         this.container = Roo.get(container);
57022         this.container.update("");
57023         this.container.setStyle("overflow", "hidden");
57024     this.container.addClass('x-grid-container');
57025
57026     this.id = this.container.id;
57027
57028     Roo.apply(this, config);
57029     // check and correct shorthanded configs
57030     
57031     var rows = [];
57032     var d =1;
57033     for (var r = 0;r < 6;r++) {
57034         
57035         rows[r]=[];
57036         for (var c =0;c < 7;c++) {
57037             rows[r][c]= '';
57038         }
57039     }
57040     if (this.eventStore) {
57041         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57042         this.eventStore.on('load',this.onLoad, this);
57043         this.eventStore.on('beforeload',this.clearEvents, this);
57044          
57045     }
57046     
57047     this.dataSource = new Roo.data.Store({
57048             proxy: new Roo.data.MemoryProxy(rows),
57049             reader: new Roo.data.ArrayReader({}, [
57050                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57051     });
57052
57053     this.dataSource.load();
57054     this.ds = this.dataSource;
57055     this.ds.xmodule = this.xmodule || false;
57056     
57057     
57058     var cellRender = function(v,x,r)
57059     {
57060         return String.format(
57061             '<div class="fc-day  fc-widget-content"><div>' +
57062                 '<div class="fc-event-container"></div>' +
57063                 '<div class="fc-day-number">{0}</div>'+
57064                 
57065                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57066             '</div></div>', v);
57067     
57068     }
57069     
57070     
57071     this.colModel = new Roo.grid.ColumnModel( [
57072         {
57073             xtype: 'ColumnModel',
57074             xns: Roo.grid,
57075             dataIndex : 'weekday0',
57076             header : 'Sunday',
57077             renderer : cellRender
57078         },
57079         {
57080             xtype: 'ColumnModel',
57081             xns: Roo.grid,
57082             dataIndex : 'weekday1',
57083             header : 'Monday',
57084             renderer : cellRender
57085         },
57086         {
57087             xtype: 'ColumnModel',
57088             xns: Roo.grid,
57089             dataIndex : 'weekday2',
57090             header : 'Tuesday',
57091             renderer : cellRender
57092         },
57093         {
57094             xtype: 'ColumnModel',
57095             xns: Roo.grid,
57096             dataIndex : 'weekday3',
57097             header : 'Wednesday',
57098             renderer : cellRender
57099         },
57100         {
57101             xtype: 'ColumnModel',
57102             xns: Roo.grid,
57103             dataIndex : 'weekday4',
57104             header : 'Thursday',
57105             renderer : cellRender
57106         },
57107         {
57108             xtype: 'ColumnModel',
57109             xns: Roo.grid,
57110             dataIndex : 'weekday5',
57111             header : 'Friday',
57112             renderer : cellRender
57113         },
57114         {
57115             xtype: 'ColumnModel',
57116             xns: Roo.grid,
57117             dataIndex : 'weekday6',
57118             header : 'Saturday',
57119             renderer : cellRender
57120         }
57121     ]);
57122     this.cm = this.colModel;
57123     this.cm.xmodule = this.xmodule || false;
57124  
57125         
57126           
57127     //this.selModel = new Roo.grid.CellSelectionModel();
57128     //this.sm = this.selModel;
57129     //this.selModel.init(this);
57130     
57131     
57132     if(this.width){
57133         this.container.setWidth(this.width);
57134     }
57135
57136     if(this.height){
57137         this.container.setHeight(this.height);
57138     }
57139     /** @private */
57140         this.addEvents({
57141         // raw events
57142         /**
57143          * @event click
57144          * The raw click event for the entire grid.
57145          * @param {Roo.EventObject} e
57146          */
57147         "click" : true,
57148         /**
57149          * @event dblclick
57150          * The raw dblclick event for the entire grid.
57151          * @param {Roo.EventObject} e
57152          */
57153         "dblclick" : true,
57154         /**
57155          * @event contextmenu
57156          * The raw contextmenu event for the entire grid.
57157          * @param {Roo.EventObject} e
57158          */
57159         "contextmenu" : true,
57160         /**
57161          * @event mousedown
57162          * The raw mousedown event for the entire grid.
57163          * @param {Roo.EventObject} e
57164          */
57165         "mousedown" : true,
57166         /**
57167          * @event mouseup
57168          * The raw mouseup event for the entire grid.
57169          * @param {Roo.EventObject} e
57170          */
57171         "mouseup" : true,
57172         /**
57173          * @event mouseover
57174          * The raw mouseover event for the entire grid.
57175          * @param {Roo.EventObject} e
57176          */
57177         "mouseover" : true,
57178         /**
57179          * @event mouseout
57180          * The raw mouseout event for the entire grid.
57181          * @param {Roo.EventObject} e
57182          */
57183         "mouseout" : true,
57184         /**
57185          * @event keypress
57186          * The raw keypress event for the entire grid.
57187          * @param {Roo.EventObject} e
57188          */
57189         "keypress" : true,
57190         /**
57191          * @event keydown
57192          * The raw keydown event for the entire grid.
57193          * @param {Roo.EventObject} e
57194          */
57195         "keydown" : true,
57196
57197         // custom events
57198
57199         /**
57200          * @event cellclick
57201          * Fires when a cell is clicked
57202          * @param {Grid} this
57203          * @param {Number} rowIndex
57204          * @param {Number} columnIndex
57205          * @param {Roo.EventObject} e
57206          */
57207         "cellclick" : true,
57208         /**
57209          * @event celldblclick
57210          * Fires when a cell is double clicked
57211          * @param {Grid} this
57212          * @param {Number} rowIndex
57213          * @param {Number} columnIndex
57214          * @param {Roo.EventObject} e
57215          */
57216         "celldblclick" : true,
57217         /**
57218          * @event rowclick
57219          * Fires when a row is clicked
57220          * @param {Grid} this
57221          * @param {Number} rowIndex
57222          * @param {Roo.EventObject} e
57223          */
57224         "rowclick" : true,
57225         /**
57226          * @event rowdblclick
57227          * Fires when a row is double clicked
57228          * @param {Grid} this
57229          * @param {Number} rowIndex
57230          * @param {Roo.EventObject} e
57231          */
57232         "rowdblclick" : true,
57233         /**
57234          * @event headerclick
57235          * Fires when a header is clicked
57236          * @param {Grid} this
57237          * @param {Number} columnIndex
57238          * @param {Roo.EventObject} e
57239          */
57240         "headerclick" : true,
57241         /**
57242          * @event headerdblclick
57243          * Fires when a header cell is double clicked
57244          * @param {Grid} this
57245          * @param {Number} columnIndex
57246          * @param {Roo.EventObject} e
57247          */
57248         "headerdblclick" : true,
57249         /**
57250          * @event rowcontextmenu
57251          * Fires when a row is right clicked
57252          * @param {Grid} this
57253          * @param {Number} rowIndex
57254          * @param {Roo.EventObject} e
57255          */
57256         "rowcontextmenu" : true,
57257         /**
57258          * @event cellcontextmenu
57259          * Fires when a cell is right clicked
57260          * @param {Grid} this
57261          * @param {Number} rowIndex
57262          * @param {Number} cellIndex
57263          * @param {Roo.EventObject} e
57264          */
57265          "cellcontextmenu" : true,
57266         /**
57267          * @event headercontextmenu
57268          * Fires when a header is right clicked
57269          * @param {Grid} this
57270          * @param {Number} columnIndex
57271          * @param {Roo.EventObject} e
57272          */
57273         "headercontextmenu" : true,
57274         /**
57275          * @event bodyscroll
57276          * Fires when the body element is scrolled
57277          * @param {Number} scrollLeft
57278          * @param {Number} scrollTop
57279          */
57280         "bodyscroll" : true,
57281         /**
57282          * @event columnresize
57283          * Fires when the user resizes a column
57284          * @param {Number} columnIndex
57285          * @param {Number} newSize
57286          */
57287         "columnresize" : true,
57288         /**
57289          * @event columnmove
57290          * Fires when the user moves a column
57291          * @param {Number} oldIndex
57292          * @param {Number} newIndex
57293          */
57294         "columnmove" : true,
57295         /**
57296          * @event startdrag
57297          * Fires when row(s) start being dragged
57298          * @param {Grid} this
57299          * @param {Roo.GridDD} dd The drag drop object
57300          * @param {event} e The raw browser event
57301          */
57302         "startdrag" : true,
57303         /**
57304          * @event enddrag
57305          * Fires when a drag operation is complete
57306          * @param {Grid} this
57307          * @param {Roo.GridDD} dd The drag drop object
57308          * @param {event} e The raw browser event
57309          */
57310         "enddrag" : true,
57311         /**
57312          * @event dragdrop
57313          * Fires when dragged row(s) are dropped on a valid DD target
57314          * @param {Grid} this
57315          * @param {Roo.GridDD} dd The drag drop object
57316          * @param {String} targetId The target drag drop object
57317          * @param {event} e The raw browser event
57318          */
57319         "dragdrop" : true,
57320         /**
57321          * @event dragover
57322          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57323          * @param {Grid} this
57324          * @param {Roo.GridDD} dd The drag drop object
57325          * @param {String} targetId The target drag drop object
57326          * @param {event} e The raw browser event
57327          */
57328         "dragover" : true,
57329         /**
57330          * @event dragenter
57331          *  Fires when the dragged row(s) first cross another DD target while being dragged
57332          * @param {Grid} this
57333          * @param {Roo.GridDD} dd The drag drop object
57334          * @param {String} targetId The target drag drop object
57335          * @param {event} e The raw browser event
57336          */
57337         "dragenter" : true,
57338         /**
57339          * @event dragout
57340          * Fires when the dragged row(s) leave another DD target while being dragged
57341          * @param {Grid} this
57342          * @param {Roo.GridDD} dd The drag drop object
57343          * @param {String} targetId The target drag drop object
57344          * @param {event} e The raw browser event
57345          */
57346         "dragout" : true,
57347         /**
57348          * @event rowclass
57349          * Fires when a row is rendered, so you can change add a style to it.
57350          * @param {GridView} gridview   The grid view
57351          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57352          */
57353         'rowclass' : true,
57354
57355         /**
57356          * @event render
57357          * Fires when the grid is rendered
57358          * @param {Grid} grid
57359          */
57360         'render' : true,
57361             /**
57362              * @event select
57363              * Fires when a date is selected
57364              * @param {DatePicker} this
57365              * @param {Date} date The selected date
57366              */
57367         'select': true,
57368         /**
57369              * @event monthchange
57370              * Fires when the displayed month changes 
57371              * @param {DatePicker} this
57372              * @param {Date} date The selected month
57373              */
57374         'monthchange': true,
57375         /**
57376              * @event evententer
57377              * Fires when mouse over an event
57378              * @param {Calendar} this
57379              * @param {event} Event
57380              */
57381         'evententer': true,
57382         /**
57383              * @event eventleave
57384              * Fires when the mouse leaves an
57385              * @param {Calendar} this
57386              * @param {event}
57387              */
57388         'eventleave': true,
57389         /**
57390              * @event eventclick
57391              * Fires when the mouse click an
57392              * @param {Calendar} this
57393              * @param {event}
57394              */
57395         'eventclick': true,
57396         /**
57397              * @event eventrender
57398              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57399              * @param {Calendar} this
57400              * @param {data} data to be modified
57401              */
57402         'eventrender': true
57403         
57404     });
57405
57406     Roo.grid.Grid.superclass.constructor.call(this);
57407     this.on('render', function() {
57408         this.view.el.addClass('x-grid-cal'); 
57409         
57410         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57411
57412     },this);
57413     
57414     if (!Roo.grid.Calendar.style) {
57415         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57416             
57417             
57418             '.x-grid-cal .x-grid-col' :  {
57419                 height: 'auto !important',
57420                 'vertical-align': 'top'
57421             },
57422             '.x-grid-cal  .fc-event-hori' : {
57423                 height: '14px'
57424             }
57425              
57426             
57427         }, Roo.id());
57428     }
57429
57430     
57431     
57432 };
57433 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57434     /**
57435      * @cfg {Store} eventStore The store that loads events.
57436      */
57437     eventStore : 25,
57438
57439      
57440     activeDate : false,
57441     startDay : 0,
57442     autoWidth : true,
57443     monitorWindowResize : false,
57444
57445     
57446     resizeColumns : function() {
57447         var col = (this.view.el.getWidth() / 7) - 3;
57448         // loop through cols, and setWidth
57449         for(var i =0 ; i < 7 ; i++){
57450             this.cm.setColumnWidth(i, col);
57451         }
57452     },
57453      setDate :function(date) {
57454         
57455         Roo.log('setDate?');
57456         
57457         this.resizeColumns();
57458         var vd = this.activeDate;
57459         this.activeDate = date;
57460 //        if(vd && this.el){
57461 //            var t = date.getTime();
57462 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57463 //                Roo.log('using add remove');
57464 //                
57465 //                this.fireEvent('monthchange', this, date);
57466 //                
57467 //                this.cells.removeClass("fc-state-highlight");
57468 //                this.cells.each(function(c){
57469 //                   if(c.dateValue == t){
57470 //                       c.addClass("fc-state-highlight");
57471 //                       setTimeout(function(){
57472 //                            try{c.dom.firstChild.focus();}catch(e){}
57473 //                       }, 50);
57474 //                       return false;
57475 //                   }
57476 //                   return true;
57477 //                });
57478 //                return;
57479 //            }
57480 //        }
57481         
57482         var days = date.getDaysInMonth();
57483         
57484         var firstOfMonth = date.getFirstDateOfMonth();
57485         var startingPos = firstOfMonth.getDay()-this.startDay;
57486         
57487         if(startingPos < this.startDay){
57488             startingPos += 7;
57489         }
57490         
57491         var pm = date.add(Date.MONTH, -1);
57492         var prevStart = pm.getDaysInMonth()-startingPos;
57493 //        
57494         
57495         
57496         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57497         
57498         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57499         //this.cells.addClassOnOver('fc-state-hover');
57500         
57501         var cells = this.cells.elements;
57502         var textEls = this.textNodes;
57503         
57504         //Roo.each(cells, function(cell){
57505         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57506         //});
57507         
57508         days += startingPos;
57509
57510         // convert everything to numbers so it's fast
57511         var day = 86400000;
57512         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57513         //Roo.log(d);
57514         //Roo.log(pm);
57515         //Roo.log(prevStart);
57516         
57517         var today = new Date().clearTime().getTime();
57518         var sel = date.clearTime().getTime();
57519         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57520         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57521         var ddMatch = this.disabledDatesRE;
57522         var ddText = this.disabledDatesText;
57523         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57524         var ddaysText = this.disabledDaysText;
57525         var format = this.format;
57526         
57527         var setCellClass = function(cal, cell){
57528             
57529             //Roo.log('set Cell Class');
57530             cell.title = "";
57531             var t = d.getTime();
57532             
57533             //Roo.log(d);
57534             
57535             
57536             cell.dateValue = t;
57537             if(t == today){
57538                 cell.className += " fc-today";
57539                 cell.className += " fc-state-highlight";
57540                 cell.title = cal.todayText;
57541             }
57542             if(t == sel){
57543                 // disable highlight in other month..
57544                 cell.className += " fc-state-highlight";
57545                 
57546             }
57547             // disabling
57548             if(t < min) {
57549                 //cell.className = " fc-state-disabled";
57550                 cell.title = cal.minText;
57551                 return;
57552             }
57553             if(t > max) {
57554                 //cell.className = " fc-state-disabled";
57555                 cell.title = cal.maxText;
57556                 return;
57557             }
57558             if(ddays){
57559                 if(ddays.indexOf(d.getDay()) != -1){
57560                     // cell.title = ddaysText;
57561                    // cell.className = " fc-state-disabled";
57562                 }
57563             }
57564             if(ddMatch && format){
57565                 var fvalue = d.dateFormat(format);
57566                 if(ddMatch.test(fvalue)){
57567                     cell.title = ddText.replace("%0", fvalue);
57568                    cell.className = " fc-state-disabled";
57569                 }
57570             }
57571             
57572             if (!cell.initialClassName) {
57573                 cell.initialClassName = cell.dom.className;
57574             }
57575             
57576             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57577         };
57578
57579         var i = 0;
57580         
57581         for(; i < startingPos; i++) {
57582             cells[i].dayName =  (++prevStart);
57583             Roo.log(textEls[i]);
57584             d.setDate(d.getDate()+1);
57585             
57586             //cells[i].className = "fc-past fc-other-month";
57587             setCellClass(this, cells[i]);
57588         }
57589         
57590         var intDay = 0;
57591         
57592         for(; i < days; i++){
57593             intDay = i - startingPos + 1;
57594             cells[i].dayName =  (intDay);
57595             d.setDate(d.getDate()+1);
57596             
57597             cells[i].className = ''; // "x-date-active";
57598             setCellClass(this, cells[i]);
57599         }
57600         var extraDays = 0;
57601         
57602         for(; i < 42; i++) {
57603             //textEls[i].innerHTML = (++extraDays);
57604             
57605             d.setDate(d.getDate()+1);
57606             cells[i].dayName = (++extraDays);
57607             cells[i].className = "fc-future fc-other-month";
57608             setCellClass(this, cells[i]);
57609         }
57610         
57611         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57612         
57613         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57614         
57615         // this will cause all the cells to mis
57616         var rows= [];
57617         var i =0;
57618         for (var r = 0;r < 6;r++) {
57619             for (var c =0;c < 7;c++) {
57620                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57621             }    
57622         }
57623         
57624         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57625         for(i=0;i<cells.length;i++) {
57626             
57627             this.cells.elements[i].dayName = cells[i].dayName ;
57628             this.cells.elements[i].className = cells[i].className;
57629             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57630             this.cells.elements[i].title = cells[i].title ;
57631             this.cells.elements[i].dateValue = cells[i].dateValue ;
57632         }
57633         
57634         
57635         
57636         
57637         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57638         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57639         
57640         ////if(totalRows != 6){
57641             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57642            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57643        // }
57644         
57645         this.fireEvent('monthchange', this, date);
57646         
57647         
57648     },
57649  /**
57650      * Returns the grid's SelectionModel.
57651      * @return {SelectionModel}
57652      */
57653     getSelectionModel : function(){
57654         if(!this.selModel){
57655             this.selModel = new Roo.grid.CellSelectionModel();
57656         }
57657         return this.selModel;
57658     },
57659
57660     load: function() {
57661         this.eventStore.load()
57662         
57663         
57664         
57665     },
57666     
57667     findCell : function(dt) {
57668         dt = dt.clearTime().getTime();
57669         var ret = false;
57670         this.cells.each(function(c){
57671             //Roo.log("check " +c.dateValue + '?=' + dt);
57672             if(c.dateValue == dt){
57673                 ret = c;
57674                 return false;
57675             }
57676             return true;
57677         });
57678         
57679         return ret;
57680     },
57681     
57682     findCells : function(rec) {
57683         var s = rec.data.start_dt.clone().clearTime().getTime();
57684        // Roo.log(s);
57685         var e= rec.data.end_dt.clone().clearTime().getTime();
57686        // Roo.log(e);
57687         var ret = [];
57688         this.cells.each(function(c){
57689              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57690             
57691             if(c.dateValue > e){
57692                 return ;
57693             }
57694             if(c.dateValue < s){
57695                 return ;
57696             }
57697             ret.push(c);
57698         });
57699         
57700         return ret;    
57701     },
57702     
57703     findBestRow: function(cells)
57704     {
57705         var ret = 0;
57706         
57707         for (var i =0 ; i < cells.length;i++) {
57708             ret  = Math.max(cells[i].rows || 0,ret);
57709         }
57710         return ret;
57711         
57712     },
57713     
57714     
57715     addItem : function(rec)
57716     {
57717         // look for vertical location slot in
57718         var cells = this.findCells(rec);
57719         
57720         rec.row = this.findBestRow(cells);
57721         
57722         // work out the location.
57723         
57724         var crow = false;
57725         var rows = [];
57726         for(var i =0; i < cells.length; i++) {
57727             if (!crow) {
57728                 crow = {
57729                     start : cells[i],
57730                     end :  cells[i]
57731                 };
57732                 continue;
57733             }
57734             if (crow.start.getY() == cells[i].getY()) {
57735                 // on same row.
57736                 crow.end = cells[i];
57737                 continue;
57738             }
57739             // different row.
57740             rows.push(crow);
57741             crow = {
57742                 start: cells[i],
57743                 end : cells[i]
57744             };
57745             
57746         }
57747         
57748         rows.push(crow);
57749         rec.els = [];
57750         rec.rows = rows;
57751         rec.cells = cells;
57752         for (var i = 0; i < cells.length;i++) {
57753             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57754             
57755         }
57756         
57757         
57758     },
57759     
57760     clearEvents: function() {
57761         
57762         if (!this.eventStore.getCount()) {
57763             return;
57764         }
57765         // reset number of rows in cells.
57766         Roo.each(this.cells.elements, function(c){
57767             c.rows = 0;
57768         });
57769         
57770         this.eventStore.each(function(e) {
57771             this.clearEvent(e);
57772         },this);
57773         
57774     },
57775     
57776     clearEvent : function(ev)
57777     {
57778         if (ev.els) {
57779             Roo.each(ev.els, function(el) {
57780                 el.un('mouseenter' ,this.onEventEnter, this);
57781                 el.un('mouseleave' ,this.onEventLeave, this);
57782                 el.remove();
57783             },this);
57784             ev.els = [];
57785         }
57786     },
57787     
57788     
57789     renderEvent : function(ev,ctr) {
57790         if (!ctr) {
57791              ctr = this.view.el.select('.fc-event-container',true).first();
57792         }
57793         
57794          
57795         this.clearEvent(ev);
57796             //code
57797        
57798         
57799         
57800         ev.els = [];
57801         var cells = ev.cells;
57802         var rows = ev.rows;
57803         this.fireEvent('eventrender', this, ev);
57804         
57805         for(var i =0; i < rows.length; i++) {
57806             
57807             cls = '';
57808             if (i == 0) {
57809                 cls += ' fc-event-start';
57810             }
57811             if ((i+1) == rows.length) {
57812                 cls += ' fc-event-end';
57813             }
57814             
57815             //Roo.log(ev.data);
57816             // how many rows should it span..
57817             var cg = this.eventTmpl.append(ctr,Roo.apply({
57818                 fccls : cls
57819                 
57820             }, ev.data) , true);
57821             
57822             
57823             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57824             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57825             cg.on('click', this.onEventClick, this, ev);
57826             
57827             ev.els.push(cg);
57828             
57829             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57830             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57831             //Roo.log(cg);
57832              
57833             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57834             cg.setWidth(ebox.right - sbox.x -2);
57835         }
57836     },
57837     
57838     renderEvents: function()
57839     {   
57840         // first make sure there is enough space..
57841         
57842         if (!this.eventTmpl) {
57843             this.eventTmpl = new Roo.Template(
57844                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57845                     '<div class="fc-event-inner">' +
57846                         '<span class="fc-event-time">{time}</span>' +
57847                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57848                     '</div>' +
57849                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57850                 '</div>'
57851             );
57852                 
57853         }
57854                
57855         
57856         
57857         this.cells.each(function(c) {
57858             //Roo.log(c.select('.fc-day-content div',true).first());
57859             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57860         });
57861         
57862         var ctr = this.view.el.select('.fc-event-container',true).first();
57863         
57864         var cls;
57865         this.eventStore.each(function(ev){
57866             
57867             this.renderEvent(ev);
57868              
57869              
57870         }, this);
57871         this.view.layout();
57872         
57873     },
57874     
57875     onEventEnter: function (e, el,event,d) {
57876         this.fireEvent('evententer', this, el, event);
57877     },
57878     
57879     onEventLeave: function (e, el,event,d) {
57880         this.fireEvent('eventleave', this, el, event);
57881     },
57882     
57883     onEventClick: function (e, el,event,d) {
57884         this.fireEvent('eventclick', this, el, event);
57885     },
57886     
57887     onMonthChange: function () {
57888         this.store.load();
57889     },
57890     
57891     onLoad: function () {
57892         
57893         //Roo.log('calendar onload');
57894 //         
57895         if(this.eventStore.getCount() > 0){
57896             
57897            
57898             
57899             this.eventStore.each(function(d){
57900                 
57901                 
57902                 // FIXME..
57903                 var add =   d.data;
57904                 if (typeof(add.end_dt) == 'undefined')  {
57905                     Roo.log("Missing End time in calendar data: ");
57906                     Roo.log(d);
57907                     return;
57908                 }
57909                 if (typeof(add.start_dt) == 'undefined')  {
57910                     Roo.log("Missing Start time in calendar data: ");
57911                     Roo.log(d);
57912                     return;
57913                 }
57914                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57915                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57916                 add.id = add.id || d.id;
57917                 add.title = add.title || '??';
57918                 
57919                 this.addItem(d);
57920                 
57921              
57922             },this);
57923         }
57924         
57925         this.renderEvents();
57926     }
57927     
57928
57929 });
57930 /*
57931  grid : {
57932                 xtype: 'Grid',
57933                 xns: Roo.grid,
57934                 listeners : {
57935                     render : function ()
57936                     {
57937                         _this.grid = this;
57938                         
57939                         if (!this.view.el.hasClass('course-timesheet')) {
57940                             this.view.el.addClass('course-timesheet');
57941                         }
57942                         if (this.tsStyle) {
57943                             this.ds.load({});
57944                             return; 
57945                         }
57946                         Roo.log('width');
57947                         Roo.log(_this.grid.view.el.getWidth());
57948                         
57949                         
57950                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57951                             '.course-timesheet .x-grid-row' : {
57952                                 height: '80px'
57953                             },
57954                             '.x-grid-row td' : {
57955                                 'vertical-align' : 0
57956                             },
57957                             '.course-edit-link' : {
57958                                 'color' : 'blue',
57959                                 'text-overflow' : 'ellipsis',
57960                                 'overflow' : 'hidden',
57961                                 'white-space' : 'nowrap',
57962                                 'cursor' : 'pointer'
57963                             },
57964                             '.sub-link' : {
57965                                 'color' : 'green'
57966                             },
57967                             '.de-act-sup-link' : {
57968                                 'color' : 'purple',
57969                                 'text-decoration' : 'line-through'
57970                             },
57971                             '.de-act-link' : {
57972                                 'color' : 'red',
57973                                 'text-decoration' : 'line-through'
57974                             },
57975                             '.course-timesheet .course-highlight' : {
57976                                 'border-top-style': 'dashed !important',
57977                                 'border-bottom-bottom': 'dashed !important'
57978                             },
57979                             '.course-timesheet .course-item' : {
57980                                 'font-family'   : 'tahoma, arial, helvetica',
57981                                 'font-size'     : '11px',
57982                                 'overflow'      : 'hidden',
57983                                 'padding-left'  : '10px',
57984                                 'padding-right' : '10px',
57985                                 'padding-top' : '10px' 
57986                             }
57987                             
57988                         }, Roo.id());
57989                                 this.ds.load({});
57990                     }
57991                 },
57992                 autoWidth : true,
57993                 monitorWindowResize : false,
57994                 cellrenderer : function(v,x,r)
57995                 {
57996                     return v;
57997                 },
57998                 sm : {
57999                     xtype: 'CellSelectionModel',
58000                     xns: Roo.grid
58001                 },
58002                 dataSource : {
58003                     xtype: 'Store',
58004                     xns: Roo.data,
58005                     listeners : {
58006                         beforeload : function (_self, options)
58007                         {
58008                             options.params = options.params || {};
58009                             options.params._month = _this.monthField.getValue();
58010                             options.params.limit = 9999;
58011                             options.params['sort'] = 'when_dt';    
58012                             options.params['dir'] = 'ASC';    
58013                             this.proxy.loadResponse = this.loadResponse;
58014                             Roo.log("load?");
58015                             //this.addColumns();
58016                         },
58017                         load : function (_self, records, options)
58018                         {
58019                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58020                                 // if you click on the translation.. you can edit it...
58021                                 var el = Roo.get(this);
58022                                 var id = el.dom.getAttribute('data-id');
58023                                 var d = el.dom.getAttribute('data-date');
58024                                 var t = el.dom.getAttribute('data-time');
58025                                 //var id = this.child('span').dom.textContent;
58026                                 
58027                                 //Roo.log(this);
58028                                 Pman.Dialog.CourseCalendar.show({
58029                                     id : id,
58030                                     when_d : d,
58031                                     when_t : t,
58032                                     productitem_active : id ? 1 : 0
58033                                 }, function() {
58034                                     _this.grid.ds.load({});
58035                                 });
58036                            
58037                            });
58038                            
58039                            _this.panel.fireEvent('resize', [ '', '' ]);
58040                         }
58041                     },
58042                     loadResponse : function(o, success, response){
58043                             // this is overridden on before load..
58044                             
58045                             Roo.log("our code?");       
58046                             //Roo.log(success);
58047                             //Roo.log(response)
58048                             delete this.activeRequest;
58049                             if(!success){
58050                                 this.fireEvent("loadexception", this, o, response);
58051                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58052                                 return;
58053                             }
58054                             var result;
58055                             try {
58056                                 result = o.reader.read(response);
58057                             }catch(e){
58058                                 Roo.log("load exception?");
58059                                 this.fireEvent("loadexception", this, o, response, e);
58060                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58061                                 return;
58062                             }
58063                             Roo.log("ready...");        
58064                             // loop through result.records;
58065                             // and set this.tdate[date] = [] << array of records..
58066                             _this.tdata  = {};
58067                             Roo.each(result.records, function(r){
58068                                 //Roo.log(r.data);
58069                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58070                                     _this.tdata[r.data.when_dt.format('j')] = [];
58071                                 }
58072                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58073                             });
58074                             
58075                             //Roo.log(_this.tdata);
58076                             
58077                             result.records = [];
58078                             result.totalRecords = 6;
58079                     
58080                             // let's generate some duumy records for the rows.
58081                             //var st = _this.dateField.getValue();
58082                             
58083                             // work out monday..
58084                             //st = st.add(Date.DAY, -1 * st.format('w'));
58085                             
58086                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58087                             
58088                             var firstOfMonth = date.getFirstDayOfMonth();
58089                             var days = date.getDaysInMonth();
58090                             var d = 1;
58091                             var firstAdded = false;
58092                             for (var i = 0; i < result.totalRecords ; i++) {
58093                                 //var d= st.add(Date.DAY, i);
58094                                 var row = {};
58095                                 var added = 0;
58096                                 for(var w = 0 ; w < 7 ; w++){
58097                                     if(!firstAdded && firstOfMonth != w){
58098                                         continue;
58099                                     }
58100                                     if(d > days){
58101                                         continue;
58102                                     }
58103                                     firstAdded = true;
58104                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58105                                     row['weekday'+w] = String.format(
58106                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58107                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58108                                                     d,
58109                                                     date.format('Y-m-')+dd
58110                                                 );
58111                                     added++;
58112                                     if(typeof(_this.tdata[d]) != 'undefined'){
58113                                         Roo.each(_this.tdata[d], function(r){
58114                                             var is_sub = '';
58115                                             var deactive = '';
58116                                             var id = r.id;
58117                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58118                                             if(r.parent_id*1>0){
58119                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58120                                                 id = r.parent_id;
58121                                             }
58122                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58123                                                 deactive = 'de-act-link';
58124                                             }
58125                                             
58126                                             row['weekday'+w] += String.format(
58127                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58128                                                     id, //0
58129                                                     r.product_id_name, //1
58130                                                     r.when_dt.format('h:ia'), //2
58131                                                     is_sub, //3
58132                                                     deactive, //4
58133                                                     desc // 5
58134                                             );
58135                                         });
58136                                     }
58137                                     d++;
58138                                 }
58139                                 
58140                                 // only do this if something added..
58141                                 if(added > 0){ 
58142                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58143                                 }
58144                                 
58145                                 
58146                                 // push it twice. (second one with an hour..
58147                                 
58148                             }
58149                             //Roo.log(result);
58150                             this.fireEvent("load", this, o, o.request.arg);
58151                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58152                         },
58153                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58154                     proxy : {
58155                         xtype: 'HttpProxy',
58156                         xns: Roo.data,
58157                         method : 'GET',
58158                         url : baseURL + '/Roo/Shop_course.php'
58159                     },
58160                     reader : {
58161                         xtype: 'JsonReader',
58162                         xns: Roo.data,
58163                         id : 'id',
58164                         fields : [
58165                             {
58166                                 'name': 'id',
58167                                 'type': 'int'
58168                             },
58169                             {
58170                                 'name': 'when_dt',
58171                                 'type': 'string'
58172                             },
58173                             {
58174                                 'name': 'end_dt',
58175                                 'type': 'string'
58176                             },
58177                             {
58178                                 'name': 'parent_id',
58179                                 'type': 'int'
58180                             },
58181                             {
58182                                 'name': 'product_id',
58183                                 'type': 'int'
58184                             },
58185                             {
58186                                 'name': 'productitem_id',
58187                                 'type': 'int'
58188                             },
58189                             {
58190                                 'name': 'guid',
58191                                 'type': 'int'
58192                             }
58193                         ]
58194                     }
58195                 },
58196                 toolbar : {
58197                     xtype: 'Toolbar',
58198                     xns: Roo,
58199                     items : [
58200                         {
58201                             xtype: 'Button',
58202                             xns: Roo.Toolbar,
58203                             listeners : {
58204                                 click : function (_self, e)
58205                                 {
58206                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58207                                     sd.setMonth(sd.getMonth()-1);
58208                                     _this.monthField.setValue(sd.format('Y-m-d'));
58209                                     _this.grid.ds.load({});
58210                                 }
58211                             },
58212                             text : "Back"
58213                         },
58214                         {
58215                             xtype: 'Separator',
58216                             xns: Roo.Toolbar
58217                         },
58218                         {
58219                             xtype: 'MonthField',
58220                             xns: Roo.form,
58221                             listeners : {
58222                                 render : function (_self)
58223                                 {
58224                                     _this.monthField = _self;
58225                                    // _this.monthField.set  today
58226                                 },
58227                                 select : function (combo, date)
58228                                 {
58229                                     _this.grid.ds.load({});
58230                                 }
58231                             },
58232                             value : (function() { return new Date(); })()
58233                         },
58234                         {
58235                             xtype: 'Separator',
58236                             xns: Roo.Toolbar
58237                         },
58238                         {
58239                             xtype: 'TextItem',
58240                             xns: Roo.Toolbar,
58241                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58242                         },
58243                         {
58244                             xtype: 'Fill',
58245                             xns: Roo.Toolbar
58246                         },
58247                         {
58248                             xtype: 'Button',
58249                             xns: Roo.Toolbar,
58250                             listeners : {
58251                                 click : function (_self, e)
58252                                 {
58253                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58254                                     sd.setMonth(sd.getMonth()+1);
58255                                     _this.monthField.setValue(sd.format('Y-m-d'));
58256                                     _this.grid.ds.load({});
58257                                 }
58258                             },
58259                             text : "Next"
58260                         }
58261                     ]
58262                 },
58263                  
58264             }
58265         };
58266         
58267         *//*
58268  * Based on:
58269  * Ext JS Library 1.1.1
58270  * Copyright(c) 2006-2007, Ext JS, LLC.
58271  *
58272  * Originally Released Under LGPL - original licence link has changed is not relivant.
58273  *
58274  * Fork - LGPL
58275  * <script type="text/javascript">
58276  */
58277  
58278 /**
58279  * @class Roo.LoadMask
58280  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58281  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58282  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58283  * element's UpdateManager load indicator and will be destroyed after the initial load.
58284  * @constructor
58285  * Create a new LoadMask
58286  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58287  * @param {Object} config The config object
58288  */
58289 Roo.LoadMask = function(el, config){
58290     this.el = Roo.get(el);
58291     Roo.apply(this, config);
58292     if(this.store){
58293         this.store.on('beforeload', this.onBeforeLoad, this);
58294         this.store.on('load', this.onLoad, this);
58295         this.store.on('loadexception', this.onLoadException, this);
58296         this.removeMask = false;
58297     }else{
58298         var um = this.el.getUpdateManager();
58299         um.showLoadIndicator = false; // disable the default indicator
58300         um.on('beforeupdate', this.onBeforeLoad, this);
58301         um.on('update', this.onLoad, this);
58302         um.on('failure', this.onLoad, this);
58303         this.removeMask = true;
58304     }
58305 };
58306
58307 Roo.LoadMask.prototype = {
58308     /**
58309      * @cfg {Boolean} removeMask
58310      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58311      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58312      */
58313     /**
58314      * @cfg {String} msg
58315      * The text to display in a centered loading message box (defaults to 'Loading...')
58316      */
58317     msg : 'Loading...',
58318     /**
58319      * @cfg {String} msgCls
58320      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58321      */
58322     msgCls : 'x-mask-loading',
58323
58324     /**
58325      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58326      * @type Boolean
58327      */
58328     disabled: false,
58329
58330     /**
58331      * Disables the mask to prevent it from being displayed
58332      */
58333     disable : function(){
58334        this.disabled = true;
58335     },
58336
58337     /**
58338      * Enables the mask so that it can be displayed
58339      */
58340     enable : function(){
58341         this.disabled = false;
58342     },
58343     
58344     onLoadException : function()
58345     {
58346         Roo.log(arguments);
58347         
58348         if (typeof(arguments[3]) != 'undefined') {
58349             Roo.MessageBox.alert("Error loading",arguments[3]);
58350         } 
58351         /*
58352         try {
58353             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58354                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58355             }   
58356         } catch(e) {
58357             
58358         }
58359         */
58360     
58361         
58362         
58363         this.el.unmask(this.removeMask);
58364     },
58365     // private
58366     onLoad : function()
58367     {
58368         this.el.unmask(this.removeMask);
58369     },
58370
58371     // private
58372     onBeforeLoad : function(){
58373         if(!this.disabled){
58374             this.el.mask(this.msg, this.msgCls);
58375         }
58376     },
58377
58378     // private
58379     destroy : function(){
58380         if(this.store){
58381             this.store.un('beforeload', this.onBeforeLoad, this);
58382             this.store.un('load', this.onLoad, this);
58383             this.store.un('loadexception', this.onLoadException, this);
58384         }else{
58385             var um = this.el.getUpdateManager();
58386             um.un('beforeupdate', this.onBeforeLoad, this);
58387             um.un('update', this.onLoad, this);
58388             um.un('failure', this.onLoad, this);
58389         }
58390     }
58391 };/*
58392  * Based on:
58393  * Ext JS Library 1.1.1
58394  * Copyright(c) 2006-2007, Ext JS, LLC.
58395  *
58396  * Originally Released Under LGPL - original licence link has changed is not relivant.
58397  *
58398  * Fork - LGPL
58399  * <script type="text/javascript">
58400  */
58401
58402
58403 /**
58404  * @class Roo.XTemplate
58405  * @extends Roo.Template
58406  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58407 <pre><code>
58408 var t = new Roo.XTemplate(
58409         '&lt;select name="{name}"&gt;',
58410                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58411         '&lt;/select&gt;'
58412 );
58413  
58414 // then append, applying the master template values
58415  </code></pre>
58416  *
58417  * Supported features:
58418  *
58419  *  Tags:
58420
58421 <pre><code>
58422       {a_variable} - output encoded.
58423       {a_variable.format:("Y-m-d")} - call a method on the variable
58424       {a_variable:raw} - unencoded output
58425       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58426       {a_variable:this.method_on_template(...)} - call a method on the template object.
58427  
58428 </code></pre>
58429  *  The tpl tag:
58430 <pre><code>
58431         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58432         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58433         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58434         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58435   
58436         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58437         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58438 </code></pre>
58439  *      
58440  */
58441 Roo.XTemplate = function()
58442 {
58443     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58444     if (this.html) {
58445         this.compile();
58446     }
58447 };
58448
58449
58450 Roo.extend(Roo.XTemplate, Roo.Template, {
58451
58452     /**
58453      * The various sub templates
58454      */
58455     tpls : false,
58456     /**
58457      *
58458      * basic tag replacing syntax
58459      * WORD:WORD()
58460      *
58461      * // you can fake an object call by doing this
58462      *  x.t:(test,tesT) 
58463      * 
58464      */
58465     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58466
58467     /**
58468      * compile the template
58469      *
58470      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58471      *
58472      */
58473     compile: function()
58474     {
58475         var s = this.html;
58476      
58477         s = ['<tpl>', s, '</tpl>'].join('');
58478     
58479         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58480             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58481             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58482             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58483             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58484             m,
58485             id     = 0,
58486             tpls   = [];
58487     
58488         while(true == !!(m = s.match(re))){
58489             var forMatch   = m[0].match(nameRe),
58490                 ifMatch   = m[0].match(ifRe),
58491                 execMatch   = m[0].match(execRe),
58492                 namedMatch   = m[0].match(namedRe),
58493                 
58494                 exp  = null, 
58495                 fn   = null,
58496                 exec = null,
58497                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58498                 
58499             if (ifMatch) {
58500                 // if - puts fn into test..
58501                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58502                 if(exp){
58503                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58504                 }
58505             }
58506             
58507             if (execMatch) {
58508                 // exec - calls a function... returns empty if true is  returned.
58509                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58510                 if(exp){
58511                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58512                 }
58513             }
58514             
58515             
58516             if (name) {
58517                 // for = 
58518                 switch(name){
58519                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58520                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58521                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58522                 }
58523             }
58524             var uid = namedMatch ? namedMatch[1] : id;
58525             
58526             
58527             tpls.push({
58528                 id:     namedMatch ? namedMatch[1] : id,
58529                 target: name,
58530                 exec:   exec,
58531                 test:   fn,
58532                 body:   m[1] || ''
58533             });
58534             if (namedMatch) {
58535                 s = s.replace(m[0], '');
58536             } else { 
58537                 s = s.replace(m[0], '{xtpl'+ id + '}');
58538             }
58539             ++id;
58540         }
58541         this.tpls = [];
58542         for(var i = tpls.length-1; i >= 0; --i){
58543             this.compileTpl(tpls[i]);
58544             this.tpls[tpls[i].id] = tpls[i];
58545         }
58546         this.master = tpls[tpls.length-1];
58547         return this;
58548     },
58549     /**
58550      * same as applyTemplate, except it's done to one of the subTemplates
58551      * when using named templates, you can do:
58552      *
58553      * var str = pl.applySubTemplate('your-name', values);
58554      *
58555      * 
58556      * @param {Number} id of the template
58557      * @param {Object} values to apply to template
58558      * @param {Object} parent (normaly the instance of this object)
58559      */
58560     applySubTemplate : function(id, values, parent)
58561     {
58562         
58563         
58564         var t = this.tpls[id];
58565         
58566         
58567         try { 
58568             if(t.test && !t.test.call(this, values, parent)){
58569                 return '';
58570             }
58571         } catch(e) {
58572             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58573             Roo.log(e.toString());
58574             Roo.log(t.test);
58575             return ''
58576         }
58577         try { 
58578             
58579             if(t.exec && t.exec.call(this, values, parent)){
58580                 return '';
58581             }
58582         } catch(e) {
58583             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58584             Roo.log(e.toString());
58585             Roo.log(t.exec);
58586             return ''
58587         }
58588         try {
58589             var vs = t.target ? t.target.call(this, values, parent) : values;
58590             parent = t.target ? values : parent;
58591             if(t.target && vs instanceof Array){
58592                 var buf = [];
58593                 for(var i = 0, len = vs.length; i < len; i++){
58594                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58595                 }
58596                 return buf.join('');
58597             }
58598             return t.compiled.call(this, vs, parent);
58599         } catch (e) {
58600             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58601             Roo.log(e.toString());
58602             Roo.log(t.compiled);
58603             return '';
58604         }
58605     },
58606
58607     compileTpl : function(tpl)
58608     {
58609         var fm = Roo.util.Format;
58610         var useF = this.disableFormats !== true;
58611         var sep = Roo.isGecko ? "+" : ",";
58612         var undef = function(str) {
58613             Roo.log("Property not found :"  + str);
58614             return '';
58615         };
58616         
58617         var fn = function(m, name, format, args)
58618         {
58619             //Roo.log(arguments);
58620             args = args ? args.replace(/\\'/g,"'") : args;
58621             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58622             if (typeof(format) == 'undefined') {
58623                 format= 'htmlEncode';
58624             }
58625             if (format == 'raw' ) {
58626                 format = false;
58627             }
58628             
58629             if(name.substr(0, 4) == 'xtpl'){
58630                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58631             }
58632             
58633             // build an array of options to determine if value is undefined..
58634             
58635             // basically get 'xxxx.yyyy' then do
58636             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58637             //    (function () { Roo.log("Property not found"); return ''; })() :
58638             //    ......
58639             
58640             var udef_ar = [];
58641             var lookfor = '';
58642             Roo.each(name.split('.'), function(st) {
58643                 lookfor += (lookfor.length ? '.': '') + st;
58644                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58645             });
58646             
58647             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58648             
58649             
58650             if(format && useF){
58651                 
58652                 args = args ? ',' + args : "";
58653                  
58654                 if(format.substr(0, 5) != "this."){
58655                     format = "fm." + format + '(';
58656                 }else{
58657                     format = 'this.call("'+ format.substr(5) + '", ';
58658                     args = ", values";
58659                 }
58660                 
58661                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58662             }
58663              
58664             if (args.length) {
58665                 // called with xxyx.yuu:(test,test)
58666                 // change to ()
58667                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58668             }
58669             // raw.. - :raw modifier..
58670             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58671             
58672         };
58673         var body;
58674         // branched to use + in gecko and [].join() in others
58675         if(Roo.isGecko){
58676             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58677                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58678                     "';};};";
58679         }else{
58680             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58681             body.push(tpl.body.replace(/(\r\n|\n)/g,
58682                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58683             body.push("'].join('');};};");
58684             body = body.join('');
58685         }
58686         
58687         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58688        
58689         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58690         eval(body);
58691         
58692         return this;
58693     },
58694
58695     applyTemplate : function(values){
58696         return this.master.compiled.call(this, values, {});
58697         //var s = this.subs;
58698     },
58699
58700     apply : function(){
58701         return this.applyTemplate.apply(this, arguments);
58702     }
58703
58704  });
58705
58706 Roo.XTemplate.from = function(el){
58707     el = Roo.getDom(el);
58708     return new Roo.XTemplate(el.value || el.innerHTML);
58709 };